├── sample ├── Add.class ├── AddMain.class ├── Add.java ├── AddMain.java └── javap_AddMain.txt ├── codecov.yaml ├── src ├── log.cpp ├── stack │ └── frame.cpp ├── main.cpp ├── execute_engine │ └── cvm_execute.cpp └── classfile │ └── classfile.cpp ├── .github ├── ISSUE_TEMPLATE │ ├── custom.md │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── macos.yml │ ├── windows.yml │ ├── ubuntu.yml │ └── release.yml ├── include └── cvm │ ├── log.hpp │ ├── stack │ ├── cvm_stack.hpp │ └── frame.hpp │ ├── fmt_commons.hpp │ ├── classfile │ ├── attribute_info.hpp │ ├── field_info.hpp │ ├── method_info.hpp │ ├── cp_info.hpp │ └── classfile.hpp │ ├── banner.hpp │ ├── cvm_commons.hpp │ └── execute_engine │ └── cvm_execute.hpp ├── cmake ├── CVMConfig.cmake.in ├── version.hpp.in ├── SourcesAndHeaders.cmake ├── Utils.cmake ├── StandardSettings.cmake └── CompilerWarnings.cmake ├── .clang-tidy ├── test ├── src │ └── tmp_test.cpp └── CMakeLists.txt ├── LICENSE ├── Makefile ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── .clang-format ├── .gitignore ├── CONTRIBUTING.md └── CMakeLists.txt /sample/Add.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lvntky/CVM/HEAD/sample/Add.class -------------------------------------------------------------------------------- /codecov.yaml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "test" 3 | 4 | comment: 5 | require_changes: true 6 | -------------------------------------------------------------------------------- /sample/AddMain.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lvntky/CVM/HEAD/sample/AddMain.class -------------------------------------------------------------------------------- /sample/Add.java: -------------------------------------------------------------------------------- 1 | public class Add { 2 | public static int add(int a, int b) { 3 | return a + b; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/log.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/cvm/log.hpp" 2 | 3 | void setLevel() { 4 | if (DEBUG_ENABLED) { 5 | spdlog::set_level(spdlog::level::debug); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /sample/AddMain.java: -------------------------------------------------------------------------------- 1 | class AddMain { 2 | 3 | public static int main(String args[]) { 4 | int a = 14; 5 | int b = 15; 6 | int c = a + b; 7 | return c; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /include/cvm/log.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __LOG_HPP__ 2 | #define __LOG_HPP__ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #define LOG_OK "OK" 9 | #define LOG_NOK "NOK" 10 | #define DEBUG_ENABLED 0 11 | 12 | void setLevel(); 13 | 14 | #endif //__LOG_HPP__ 15 | -------------------------------------------------------------------------------- /cmake/CVMConfig.cmake.in: -------------------------------------------------------------------------------- 1 | set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@) 2 | 3 | @PACKAGE_INIT@ 4 | 5 | set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@CMAKE_INSTALL_FULL_INCLUDEDIR@") 6 | 7 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 8 | 9 | check_required_components(@PROJECT_NAME@) 10 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*,-fuchsia-*,-google-*,-zircon-*,-abseil-*,-modernize-use-trailing-return-type,-llvm-*,-llvmlibc-*' 3 | CheckOptions: [{ key: misc-non-private-member-variables-in-classes, value: IgnoreClassesWithAllMemberVariablesBeingPublic }] 4 | WarningsAsErrors: '*' 5 | HeaderFilterRegex: '' 6 | FormatStyle: none 7 | -------------------------------------------------------------------------------- /test/src/tmp_test.cpp: -------------------------------------------------------------------------------- 1 | //#include "cvm/tmp.hpp" 2 | 3 | #include 4 | 5 | TEST(TmpAddTest, CheckValues) 6 | { 7 | //ASSERT_EQ(tmp::add(1, 2), 3); 8 | EXPECT_TRUE(true); 9 | } 10 | 11 | int main(int argc, char **argv) 12 | { 13 | ::testing::InitGoogleTest(&argc, argv); 14 | return RUN_ALL_TESTS(); 15 | } 16 | -------------------------------------------------------------------------------- /include/cvm/stack/cvm_stack.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __CVM_STACK_HPP__ 2 | #define __CVM_STACK_HPP__ 3 | 4 | #include 5 | 6 | #include "frame.hpp" 7 | 8 | class CVMStack { 9 | public: 10 | void pushFrame(Frame frame) { frames.push(frame); } 11 | 12 | Frame& topFrame() { return frames.top(); } 13 | 14 | void popFrame() { frames.pop(); } 15 | 16 | private: 17 | std::stack frames; 18 | }; 19 | 20 | #endif //__CVM_STACK_HPP__ 21 | -------------------------------------------------------------------------------- /cmake/version.hpp.in: -------------------------------------------------------------------------------- 1 | #ifndef @PROJECT_NAME_UPPERCASE@_VERSION_H_ 2 | #define @PROJECT_NAME_UPPERCASE@_VERSION_H_ 3 | 4 | #define @PROJECT_NAME_UPPERCASE@_VERSION "@PROJECT_VERSION@" 5 | 6 | #define @PROJECT_NAME_UPPERCASE@_MAJOR_VERSION @PROJECT_VERSION_MAJOR@ 7 | #define @PROJECT_NAME_UPPERCASE@_MINOR_VERSION @PROJECT_VERSION_MINOR@ 8 | #define @PROJECT_NAME_UPPERCASE@_PATCH_VERSION @PROJECT_VERSION_PATCH@ 9 | 10 | #endif // @PROJECT_NAME_UPPERCASE@_VERSION_H_ 11 | 12 | -------------------------------------------------------------------------------- /include/cvm/fmt_commons.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FMT_COMMONS_HPP_ 2 | #define FMT_COMMONS_HPP_ 3 | 4 | /** 5 | * 6 | * A colleciton of fmt API libraries 7 | * might not be a good idea from compiler view 8 | * but handy when coding the CVMpp :3 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #endif // FMT_COMMONS_HPP_ 22 | -------------------------------------------------------------------------------- /include/cvm/classfile/attribute_info.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __ATTRIBUTE_INFO_HPP__ 2 | #define __ATTRIBUTE_INFO_HPP__ 3 | 4 | #include 5 | 6 | #include "../fmt_commons.hpp" 7 | 8 | typedef struct attribute_info { 9 | uint16_t attribute_name_index; 10 | uint32_t attribute_length; 11 | uint8_t* info; 12 | 13 | ~attribute_info() { delete[] info; } 14 | 15 | std::string to_string() const { 16 | return fmt::format( 17 | "Attribute:\n" 18 | " attribute_name_index: {}\n" 19 | " attribute_length: {}\n" 20 | " info: [{}]", 21 | attribute_name_index, attribute_length, 22 | fmt::join(info, info + attribute_length, ", ")); 23 | } 24 | } attribute_info; 25 | 26 | #endif //__ATTRIBUTE_INFO_HPP__ 27 | -------------------------------------------------------------------------------- /cmake/SourcesAndHeaders.cmake: -------------------------------------------------------------------------------- 1 | set(sources 2 | src/log.cpp 3 | src/classfile/classfile.cpp 4 | src/stack/frame.cpp 5 | src/execute_engine/cvm_execute.cpp 6 | 7 | ) 8 | 9 | set(exe_sources 10 | src/main.cpp 11 | ${sources} 12 | ) 13 | 14 | set(headers 15 | include/cvm/fmt_commons.hpp 16 | include/cvm/log.hpp 17 | include/cvm/banner.hpp 18 | include/cvm/classfile/classfile.hpp 19 | include/cvm/classfile/cp_info.hpp 20 | include/cvm/classfile/attribute_info.hpp 21 | include/cvm/classfile/method_info.hpp 22 | include/cvm/classfile/field_info.hpp 23 | include/cvm/stack/frame.hpp 24 | include/cvm/stack/cvm_stack.hpp 25 | include/cvm/execute_engine/cvm_execute.hpp 26 | ) 27 | 28 | 29 | set(test_sources 30 | src/tmp_test.cpp 31 | ) 32 | -------------------------------------------------------------------------------- /src/stack/frame.cpp: -------------------------------------------------------------------------------- 1 | #include "../../include/cvm/stack/frame.hpp" 2 | 3 | int32_t Frame::getLocalVariable(size_t index) const { 4 | return localVariables.at(index); 5 | } 6 | void Frame::setLocalVariable(size_t index, int32_t value) { 7 | localVariables.at(index) = value; 8 | } 9 | 10 | void Frame::pushOperand(int32_t value) { 11 | if (operandStackSize >= operandStack.size()) { 12 | throw std::overflow_error("Operand stack overflow"); 13 | } 14 | operandStack[operandStackSize++] = value; 15 | } 16 | 17 | int32_t Frame::popOperand() { 18 | if (operandStackSize == 0) { 19 | throw std::underflow_error("Operand stack underflow"); 20 | } 21 | return operandStack[--operandStackSize]; 22 | } 23 | 24 | size_t Frame::getOperandStackSize() { 25 | return operandStackSize; 26 | } 27 | -------------------------------------------------------------------------------- /include/cvm/banner.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __BANNER_HPP__ 2 | #define __BANNER_HPP__ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #define CVM_VERSION "2.0.0" 9 | 10 | void printBanner() { 11 | const std::string green = "\033[32m"; 12 | const std::string reset = "\033[0m"; 13 | 14 | const std::string banner = R"( 15 | /$$$$$$ /$$ /$$ /$$ /$$ 16 | /$$__ $$| $$ | $$| $$$ /$$$ 17 | | $$ \__/| $$ | $$| $$$$ /$$$$ 18 | | $$ | $$ / $$/| $$ $$/$$ $$ 19 | | $$ \ $$ $$/ | $$ $$$| $$ 20 | | $$ $$ \ $$$/ | $$\ $ | $$ 21 | | $$$$$$/ \ $/ | $$ \/ | $$ 22 | \______/ \_/ |__/ |__/ 23 | 24 | )"; 25 | 26 | fmt::print("{}{}{}", green, banner, reset); 27 | fmt::print("Version: {}\n\n", CVM_VERSION); 28 | } 29 | 30 | #endif // __BANNER_HPP__ 31 | -------------------------------------------------------------------------------- /include/cvm/cvm_commons.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __CVM_COMMONS_HPP__ 2 | #define __CVM_COMMONS_HPP__ 3 | 4 | /** 5 | * This header mostly contains 6 | * JVM spec to C++ convertions 7 | * i.e Oracle's JVM spec uses uint32_t as u4 8 | * So i created this header to convert this type of values to C++. 9 | * Not mandatory to use and maybe confusing sometimes 10 | * But I liked to add it in order to be compatible with JVM spec 11 | * Referance : https://docs.oracle.com/javase/specs/jvms/se8/html/ 12 | */ 13 | 14 | 15 | #include 16 | 17 | using u1 = uint8_t; // JVM unsigned byte (8 bits) 18 | using u2 = uint16_t; // JVM unsigned short (16 bits) 19 | using u4 = uint32_t; // JVM unsigned int (32 bits) 20 | using u8 = uint64_t; // JVM unsigned long long (64 bits) 21 | 22 | #endif //__CVM_COMMONS_HPP__ 23 | -------------------------------------------------------------------------------- /include/cvm/stack/frame.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __FRAME_HPP__ 2 | #define __FRAME_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../fmt_commons.hpp" 9 | // #include "../log.hpp" 10 | 11 | class Frame { 12 | public: 13 | Frame(size_t maxLocals, size_t maxStack) 14 | : localVariables(maxLocals), 15 | operandStack(maxStack), 16 | operandStackSize(maxStack) {} 17 | int32_t getLocalVariable(size_t index) const; 18 | void setLocalVariable(size_t index, int32_t value); 19 | void pushOperand(int32_t value); 20 | int32_t popOperand(); 21 | size_t getOperandStackSize(); 22 | 23 | private: 24 | std::vector localVariables; 25 | std::vector operandStack; 26 | size_t operandStackSize; 27 | }; 28 | 29 | #endif //__FRAME_HPP__ 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help me improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | 1. Go to '...' 17 | 2. Click on '....' 18 | 3. Scroll down to '....' 19 | 4. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **Desktop (please complete the following information):** 28 | 29 | * OS: [e.g. Windows] 30 | * Version [e.g. 10] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated 12 | when [...] 13 | 14 | **Describe the solution you'd like** 15 | A clear and concise description of what you want to happen. 16 | 17 | **Describe alternatives you've considered** 18 | A clear and concise description of any alternative solutions or features you've considered. 19 | 20 | **Provide usage examples** 21 | A few examples of how the feature should be used. Please make sure they are clear 22 | and concise. 23 | 24 | **Additional context** 25 | Add any other context or screenshots about the feature request here. 26 | -------------------------------------------------------------------------------- /include/cvm/execute_engine/cvm_execute.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __CVM_EXECUTE_HPP__ 2 | #define __CVM_EXECUTE_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../classfile/classfile.hpp" 9 | #include "../log.hpp" 10 | 11 | class CVM { 12 | public: 13 | CVM() = default; 14 | void execute(const Classfile& cf, const std::string& methodName); 15 | std::string return_value; 16 | 17 | private: 18 | std::string getUtf8FromConstantPool(const Classfile& cf, uint16_t index); 19 | const method_info* findMehodByName(const Classfile& cf, 20 | const std::string& methodName); 21 | const uint8_t* getByteCode(const Classfile& cf, 22 | const method_info* methodInfo); 23 | void interprete(const uint8_t* byteCode, const Classfile& cf); 24 | }; 25 | 26 | #endif //__CVM_EXECUTE_HPP__ 27 | -------------------------------------------------------------------------------- /include/cvm/classfile/field_info.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __FIELD_INFO_HPP__ 2 | #define __FIELD_INFO_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | #include "../fmt_commons.hpp" 8 | #include "attribute_info.hpp" 9 | 10 | typedef struct field_info { 11 | uint16_t access_flags; 12 | uint16_t name_index; 13 | uint16_t descriptor_index; 14 | uint16_t attributes_count; 15 | std::vector attributes; 16 | 17 | std::string to_string() const { 18 | std::string attributes_str; 19 | for (const auto& attr : attributes) { 20 | attributes_str += attr.to_string() + "\n"; 21 | } 22 | return fmt::format( 23 | "Field:\n" 24 | " access_flags: 0x{:04x}\n" 25 | " name_index: {}\n" 26 | " descriptor_index: {}\n" 27 | " attributes_count: {}\n" 28 | " attributes: [\n{}\n ]", 29 | access_flags, name_index, descriptor_index, attributes_count, 30 | attributes_str); 31 | } 32 | } field_info; 33 | 34 | #endif //__FIELD_INFO_HPP__ 35 | -------------------------------------------------------------------------------- /include/cvm/classfile/method_info.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __METHOD_INFO_HPP__ 2 | #define __METHOD_INFO_HPP__ 3 | 4 | #include 5 | #include 6 | 7 | #include "../fmt_commons.hpp" 8 | #include "attribute_info.hpp" 9 | 10 | typedef struct method_info { 11 | uint16_t access_flags; 12 | uint16_t name_index; 13 | uint16_t descriptor_index; 14 | uint16_t attributes_count; 15 | std::vector attributes; 16 | 17 | std::string to_string() const { 18 | std::string attributes_str; 19 | for (const auto& attr : attributes) { 20 | attributes_str += attr.to_string() + "\n"; 21 | } 22 | return fmt::format( 23 | "Method:\n" 24 | " access_flags: 0x{:04x}\n" 25 | " name_index: {}\n" 26 | " descriptor_index: {}\n" 27 | " attributes_count: {}\n" 28 | " attributes: [\n{}\n ]", 29 | access_flags, name_index, descriptor_index, attributes_count, 30 | attributes_str); 31 | } 32 | } method_info; 33 | 34 | #endif //__METHOD_INFO_HPP__ 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c)2023 Levent Kaya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /include/cvm/classfile/cp_info.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __CP_INF0_HPP__ 2 | #define __CP_INF0_HPP__ 3 | 4 | #include 5 | 6 | #include "../fmt_commons.hpp" 7 | 8 | // Constants for constant pool tags 9 | #define CONSTANT_Class 7 10 | #define CONSTANT_Fieldref 9 11 | #define CONSTANT_Methodref 10 12 | #define CONSTANT_InterfaceMethodref 11 13 | #define CONSTANT_String 8 14 | #define CONSTANT_Integer 3 15 | #define CONSTANT_NameAndType 12 16 | #define CONSTANT_Double 6 17 | #define CONSTANT_Utf8 1 18 | 19 | typedef struct cpinfo { 20 | uint8_t tag; 21 | union { 22 | struct { 23 | uint16_t name_index; 24 | } Class; 25 | struct { 26 | uint16_t string_index; 27 | } String; 28 | struct { 29 | uint16_t class_index; 30 | uint16_t name_and_type_index; 31 | } Fieldref, Methodref, InterfaceMethodref; 32 | struct { 33 | int32_t bytes; 34 | } Integer; 35 | struct { 36 | uint16_t name_index; 37 | uint16_t descriptor_index; 38 | } NameAndType; 39 | struct { 40 | uint64_t bytes; 41 | } Double; 42 | struct { 43 | uint16_t length; 44 | uint8_t* bytes; 45 | } Utf8; 46 | } info; 47 | } cp_info; 48 | 49 | #endif //__CP_INF0_HPP__ 50 | -------------------------------------------------------------------------------- /include/cvm/classfile/classfile.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __CLASSFILE_HPP__ 2 | #define __CLASSFILE_HPP__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../fmt_commons.hpp" 10 | #include "attribute_info.hpp" 11 | #include "cp_info.hpp" 12 | #include "field_info.hpp" 13 | #include "method_info.hpp" 14 | 15 | class Classfile { 16 | public: 17 | uint32_t magic; 18 | uint16_t minor_version; 19 | uint16_t major_version; 20 | uint16_t constant_pool_count; 21 | std::vector constant_pool; 22 | uint16_t access_flags; 23 | uint16_t this_class; 24 | uint16_t super_class; 25 | uint16_t interfaces_count; 26 | std::vector interfaces; 27 | uint16_t fields_count; 28 | std::vector fields; 29 | uint16_t methods_count; 30 | std::vector methods; 31 | uint16_t attributes_count; 32 | std::vector attributes; 33 | 34 | uint16_t readShort(const uint8_t* bytes, size_t& offset); 35 | uint32_t readInt(const uint8_t* bytes, size_t& offset); 36 | uint64_t readLong(const uint8_t* bytes, size_t& offset); 37 | double readDouble(const uint8_t* bytes, size_t& offset); 38 | Classfile parseClassfile(const uint8_t* classBytes, size_t fileSize); 39 | std::string to_string() const; 40 | }; 41 | 42 | #endif //__CLASSFILE_HPP__ 43 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/cvm/banner.hpp" 2 | #include "../include/cvm/classfile/classfile.hpp" 3 | #include "../include/cvm/execute_engine/cvm_execute.hpp" 4 | #include "../include/cvm/fmt_commons.hpp" 5 | #include "../include/cvm/stack/cvm_stack.hpp" 6 | #include "../include/cvm/stack/frame.hpp" 7 | 8 | int main(int argc, char** argv) 9 | 10 | { 11 | printBanner(); 12 | 13 | if (argc < 2) { 14 | throw std::runtime_error("Please specify .class file path as an argument."); 15 | } 16 | 17 | std::string classFilePath = argv[1]; 18 | std::ifstream classFile(classFilePath, std::ios::binary); 19 | 20 | if (!classFile) { 21 | throw std::runtime_error("Cannot open class file at specified path."); 22 | } 23 | 24 | classFile.seekg(0, std::ios::end); 25 | size_t fileSize = classFile.tellg(); 26 | classFile.seekg(0, std::ios::beg); 27 | 28 | std::vector buffer(fileSize); 29 | if (!classFile.read(reinterpret_cast(buffer.data()), fileSize)) { 30 | throw std::runtime_error("Error while reading class data."); 31 | return 1; 32 | } 33 | 34 | Classfile cf; 35 | cf = cf.parseClassfile(buffer.data(), fileSize); 36 | 37 | CVM cvm; 38 | cvm.execute(cf, "main"); 39 | 40 | fmt::print( 41 | "\n\nCVM successfully terminated program: {} with return value : {}\n", 42 | argv[1], cvm.return_value); 43 | 44 | classFile.close(); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /cmake/Utils.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Print a message only if the `VERBOSE_OUTPUT` option is on 3 | # 4 | 5 | function(verbose_message content) 6 | if(${PROJECT_NAME}_VERBOSE_OUTPUT) 7 | message(STATUS ${content}) 8 | endif() 9 | endfunction() 10 | 11 | # 12 | # Add a target for formating the project using `clang-format` (i.e: cmake --build build --target clang-format) 13 | # 14 | 15 | function(add_clang_format_target) 16 | if(NOT ${PROJECT_NAME}_CLANG_FORMAT_BINARY) 17 | find_program(${PROJECT_NAME}_CLANG_FORMAT_BINARY clang-format) 18 | endif() 19 | 20 | if(${PROJECT_NAME}_CLANG_FORMAT_BINARY) 21 | if(${PROJECT_NAME}_BUILD_EXECUTABLE) 22 | add_custom_target(clang-format 23 | COMMAND ${${PROJECT_NAME}_CLANG_FORMAT_BINARY} 24 | -i ${exe_sources} ${headers} 25 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 26 | elseif(${PROJECT_NAME}_BUILD_HEADERS_ONLY) 27 | add_custom_target(clang-format 28 | COMMAND ${${PROJECT_NAME}_CLANG_FORMAT_BINARY} 29 | -i ${headers} 30 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 31 | else() 32 | add_custom_target(clang-format 33 | COMMAND ${${PROJECT_NAME}_CLANG_FORMAT_BINARY} 34 | -i ${sources} ${headers} 35 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 36 | endif() 37 | 38 | message(STATUS "Format the project using the `clang-format` target (i.e: cmake --build build --target clang-format).\n") 39 | endif() 40 | endfunction() 41 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: MacOS 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | INSTALL_LOCATION: .local 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: macos-latest 17 | if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - name: cache dependencies 23 | uses: actions/cache@v2 24 | id: cache 25 | with: 26 | path: ${{ github.workspace }}/${{ env.INSTALL_LOCATION }} 27 | key: ${{ runner.os }}-dependencies 28 | 29 | - name: install GoogleTest 30 | if: ${{ steps.cache.output.cache-hit != 'true' }} 31 | run: | 32 | cd .. 33 | git clone https://github.com/google/googletest.git --branch main 34 | cd googletest 35 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/$INSTALL_LOCATION 36 | cmake --build build --config Release 37 | cmake --build build --target install --config Release 38 | cd ../CVM 39 | 40 | - name: configure 41 | run: cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/$INSTALL_LOCATION 42 | 43 | - name: build 44 | run: cmake --build build --config $BUILD_TYPE -j4 45 | 46 | - name: run tests 47 | run: | 48 | cd build 49 | ctest -C $BUILD_TYPE -VV 50 | 51 | - name: install project 52 | run: cmake --build build --target install --config Release 53 | 54 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Windows 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | INSTALL_LOCATION: ".local" 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: windows-latest 17 | if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - name: cache dependencies 23 | uses: actions/cache@v2 24 | id: cache 25 | with: 26 | path: ${{env.INSTALL_LOCATION}} 27 | key: ${{runner.os}}-dependencies 28 | 29 | - name: install GoogleTest 30 | if: ${{steps.cache.output.cache-hit != 'true'}} 31 | run: | 32 | cd .. 33 | git clone https://github.com/google/googletest.git --branch main 34 | cd googletest 35 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX="$HOME/$env:INSTALL_LOCATION" -Dgtest_force_shared_crt=1 36 | cmake --build build --config Release 37 | cmake --build build --target install --config Release 38 | cd ../CVM 39 | 40 | - name: configure 41 | run: cmake -Bbuild -DCMAKE_INSTALL_PREFIX="$HOME/$env:INSTALL_LOCATION" 42 | 43 | - name: build 44 | run: cmake --build build --config "$env:BUILD_TYPE" -j4 45 | 46 | - name: run tests 47 | run: | 48 | cd build 49 | ctest -C "$env:BUILD_TYPE" -VV 50 | 51 | - name: install project 52 | run: cmake --build build --target install --config Release 53 | 54 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: Ubuntu 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | INSTALL_LOCATION: .local 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ubuntu-latest 17 | if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - name: cache dependencies 23 | uses: actions/cache@v2 24 | id: cache 25 | with: 26 | path: ${{ github.workspace }}/${{ env.INSTALL_LOCATION }} 27 | key: ${{ runner.os }}-dependencies 28 | 29 | - name: install GoogleTest 30 | if: ${{ steps.cache.output.cache-hit != 'true' }} 31 | run: | 32 | cd .. 33 | git clone https://github.com/google/googletest.git --branch main 34 | cd googletest 35 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/$INSTALL_LOCATION 36 | cmake --build build --config Release 37 | cmake --build build --target install --config Release 38 | 39 | - name: configure 40 | run: cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/$INSTALL_LOCATION -DProject_ENABLE_CODE_COVERAGE=1 41 | 42 | - name: build 43 | run: cmake --build build --config $BUILD_TYPE -j4 44 | 45 | - name: run tests 46 | run: | 47 | cd build 48 | ctest -C $BUILD_TYPE -VV 49 | 50 | - name: Code coverage using Codecov 51 | run: bash <(curl -s https://codecov.io/bash) 52 | 53 | - name: install project 54 | run: cmake --build build --target install --config Release 55 | 56 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install coverage test docs help 2 | .DEFAULT_GOAL := help 3 | 4 | define BROWSER_PYSCRIPT 5 | import os, webbrowser, sys 6 | 7 | try: 8 | from urllib import pathname2url 9 | except: 10 | from urllib.request import pathname2url 11 | 12 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) 13 | endef 14 | export BROWSER_PYSCRIPT 15 | 16 | define PRINT_HELP_PYSCRIPT 17 | import re, sys 18 | 19 | for line in sys.stdin: 20 | match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) 21 | if match: 22 | target, help = match.groups() 23 | print("%-20s %s" % (target, help)) 24 | endef 25 | export PRINT_HELP_PYSCRIPT 26 | 27 | BROWSER := python -c "$$BROWSER_PYSCRIPT" 28 | INSTALL_LOCATION := ~/.local 29 | 30 | help: 31 | @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) 32 | 33 | test: ## run tests quickly with ctest 34 | rm -rf build/ 35 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) -Dmodern-cpp-template_ENABLE_UNIT_TESTING=1 -DCMAKE_BUILD_TYPE="Release" 36 | cmake --build build --config Release 37 | cd build/ && ctest -C Release -VV 38 | 39 | coverage: ## check code coverage quickly GCC 40 | rm -rf build/ 41 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) -Dmodern-cpp-template_ENABLE_CODE_COVERAGE=1 42 | cmake --build build --config Release 43 | cd build/ && ctest -C Release -VV 44 | cd .. && (bash -c "find . -type f -name '*.gcno' -exec gcov -pb {} +" || true) 45 | 46 | docs: ## generate Doxygen HTML documentation, including API docs 47 | rm -rf docs/ 48 | rm -rf build/ 49 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) -DProject_ENABLE_DOXYGEN=1 50 | cmake --build build --config Release 51 | cmake --build build --target doxygen-docs 52 | $(BROWSER) docs/html/index.html 53 | 54 | install: ## install the package to the `INSTALL_LOCATION` 55 | rm -rf build/ 56 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) 57 | cmake --build build --config Release 58 | cmake --build build --target install --config Release 59 | 60 | format: ## format the project sources 61 | rm -rf build/ 62 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) 63 | cmake --build build --target clang-format 64 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | # 4 | # Project details 5 | # 6 | 7 | project( 8 | ${CMAKE_PROJECT_NAME}Tests 9 | LANGUAGES CXX 10 | ) 11 | 12 | verbose_message("Adding tests under ${CMAKE_PROJECT_NAME}Tests...") 13 | 14 | foreach(file ${test_sources}) 15 | string(REGEX REPLACE "(.*/)([a-zA-Z0-9_ ]+)(\.cpp)" "\\2" test_name ${file}) 16 | add_executable(${test_name}_Tests ${file}) 17 | 18 | # 19 | # Set the compiler standard 20 | # 21 | 22 | target_compile_features(${test_name}_Tests PUBLIC cxx_std_17) 23 | 24 | # 25 | # Setup code coverage if enabled 26 | # 27 | 28 | if (${CMAKE_PROJECT_NAME}_ENABLE_CODE_COVERAGE) 29 | target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC -O0 -g -fprofile-arcs -ftest-coverage) 30 | target_link_options(${CMAKE_PROJECT_NAME} PUBLIC -fprofile-arcs -ftest-coverage) 31 | verbose_message("Code coverage is enabled and provided with GCC.") 32 | endif() 33 | 34 | # 35 | # Load the desired unit testing framework 36 | # 37 | # Currently supported: GoogleTest (and GoogleMock), Catch2. 38 | 39 | if(${CMAKE_PROJECT_NAME}_BUILD_EXECUTABLE) 40 | set(${CMAKE_PROJECT_NAME}_TEST_LIB ${CMAKE_PROJECT_NAME}_LIB) 41 | else() 42 | set(${CMAKE_PROJECT_NAME}_TEST_LIB ${CMAKE_PROJECT_NAME}) 43 | endif() 44 | 45 | if(${CMAKE_PROJECT_NAME}_USE_GTEST) 46 | find_package(GTest REQUIRED) 47 | 48 | if(${CMAKE_PROJECT_NAME}_USE_GOOGLE_MOCK) 49 | set(GOOGLE_MOCK_LIBRARIES GTest::gmock GTest::gmock_main) 50 | endif() 51 | 52 | target_link_libraries( 53 | ${test_name}_Tests 54 | PUBLIC 55 | GTest::GTest 56 | GTest::Main 57 | ${GOOGLE_MOCK_LIBRARIES} 58 | ${${CMAKE_PROJECT_NAME}_TEST_LIB} 59 | ) 60 | elseif(${CMAKE_PROJECT_NAME}_USE_CATCH2) 61 | find_package(Catch2 REQUIRED) 62 | target_link_libraries( 63 | ${test_name}_Tests 64 | PUBLIC 65 | Catch2::Catch2 66 | ${${CMAKE_PROJECT_NAME}_TEST_LIB} 67 | ) 68 | else() 69 | message(FATAL_ERROR "Unknown testing library. Please setup your desired unit testing library by using `target_link_libraries`.") 70 | endif() 71 | 72 | # 73 | # Add the unit tests 74 | # 75 | 76 | add_test( 77 | NAME 78 | ${test_name} 79 | COMMAND 80 | ${test_name}_Tests 81 | ) 82 | endforeach() 83 | 84 | verbose_message("Finished adding unit tests for ${CMAKE_PROJECT_NAME}.") 85 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **IMPORTANT: Please do not create a Pull Request without creating an issue first.** 2 | 3 | *Any change needs to be discussed before proceeding. Failure to do so may result 4 | in the rejection of the pull request.* 5 | 6 | Please provide enough information so that others can review your pull request. You 7 | can skip this if you're fixing a typo or adding an app to the Showcase. 8 | 9 | Explain the **details** for making this change. What existing problem does the pull 10 | request solve? 11 | 12 | Ex: 13 | 14 | 1. If you "Added a/changed the function to do X", explain why: 15 | 16 | * it is necessary to have a way to do X. 17 | * if there already exists a way, why is your implementation better 18 | 19 | 2. If you "Fixed bug/error in X", explain: 20 | 21 | * what was the bug/error (if you already made an issue, please link to it here) 22 | * how does your implementation fix the issue 23 | 24 | ### Code style and formatting 25 | 26 | Check the [Contributors Style Guidelines section](CONTRIBUTING.md#Style-guidelines) 27 | for how to write your code and the [Contributors Code Formatting section](CONTRIBUTING.md#Code-formatting) 28 | for how to format your code. 29 | 30 | #### Closing Issues 31 | 32 | Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes 33 | (if such). 34 | 35 | --- 36 | 37 | Fixes #XXXX 38 | 39 | ## Proposed changes 40 | 41 | * 42 | * 43 | * 44 | 45 | ## Motivation behind changes 46 | 47 | ### Test plan 48 | 49 | Demonstrate the code is solid. Example: The exact commands you ran and their output, 50 | screenshots / videos if the pull request changes UI. 51 | 52 | *Make sure tests pass on all of the [relevant CI workflows](https://github.com/filipdutescu/modern-cpp-template/actions).* 53 | 54 | ### Pull Request Readiness Checklist 55 | 56 | See details at [CONTRIBUTING.md](https://github.com/filipdutescu/modern-cpp-template/blob/master/CONTRIBUTING.md). 57 | 58 | * [ ] I agree to contribute to the project under [INSERT PROJECT NAME] (Unlicense) 59 | [License](LICENSE). 60 | 61 | * [ ] To the best of my knowledge, the proposed patch is not based on a code under 62 | GPL or other license that is incompatible with [INSERT PROJECT NAME] 63 | 64 | * [ ] The PR is proposed to proper branch 65 | 66 | * [ ] There is reference to original bug report and related work 67 | 68 | * [ ] There is accuracy test, performance test and test data in the repository, 69 | if applicable 70 | 71 | * [ ] The feature is well documented and sample code can be built with the project 72 | CMake 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CVM 2 | ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/lvntky/CVM) 3 | ![GitHub](https://img.shields.io/github/license/lvntky/cvm) 4 | ![GitHub issues](https://img.shields.io/github/issues/lvntky/cvm) 5 | 6 |

7 | Graphic design is my passion 8 |

9 | 10 |
11 | 12 | >**This is the second re-write of the CVM.** 13 | 14 | 15 | >**The first implementation of the CVM has been made in C which can be reachable at [cvm/archive](https://github.com/lvntky/CVM/tree/archive) for archive purposes Since the first implementation does not work as intended** 16 | 17 | 18 | >**The master branch is the current and active version of the CVM which is rewrite in C++** 19 | 20 | A toy Java Virtual Machine coded in C(and C++). CVM is a simple and lightweight JVM that aims to provide a basic understanding of JVM internals and bytecode execution. This project serves as an educational and fun resource for those interested in learning about JVM implementation details. 21 | 22 | ## Features 23 | 24 | - Implementation of a minimalistic JVM in C 25 | - Support for executing Java bytecode 26 | - Basic class loading and method execution 27 | - Instruction set interpretation 28 | 29 | ## Getting Started 30 | 31 | Follow these steps to get started with CVM: 32 | 33 | 1. **Clone the Repository:** 34 | 35 | ```bash 36 | git clone https://github.com/lvntky/CVM.git 37 | cd CVM 38 | ``` 39 | 2. **Compile the Source Code:** 40 | 41 | ```bash 42 | mkdir build 43 | make install 44 | ``` 45 | 3. **Run a Java Program:** 46 | 47 | ```bash 48 | ./cvm JavaProgram.class 49 | ``` 50 | 51 | ## Usage 52 | 53 | CVM accepts a Java class file as a command-line argument. It loads and executes the main method of the specified class. 54 | 55 | Example: 56 | ```bash 57 | ./cvm HelloWorld.class 58 | ``` 59 | 60 | ## Contributing 61 | 62 | We welcome contributions to CVM. If you want to add new features, fix bugs, or improve documentation, please follow these steps: 63 | 64 | 1. Fork the repository. 65 | 2. Create a new branch for your feature or bug fix. 66 | 3. Make your changes and commit them. 67 | 4. Open a pull request explaining your changes. 68 | 69 | ## License 70 | 71 | CVM is licensed under the MIT License. See the LICENSE file for details. 72 | 73 | ## Acknowledgments 74 | 75 | CVM was inspired by a passion for JVM internals and bytecode execution. I would like to thank the Java community for providing valuable resources and documentation on JVM internals. 76 | 77 | - [Oracle's JVM Specification](https://docs.oracle.com/javase/specs/jvms/se8/html/) 78 | 79 | ## Contact 80 | 81 | If you have questions or need assistance with CVM, feel free to contact me at leventkayadev@gmail.com . 82 | 83 | Happy coding! 🚀 84 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Google C/C++ Code Style settings 2 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 3 | # Author: Kehan Xue, kehan.xue (at) gmail.com 4 | 5 | Language: Cpp 6 | BasedOnStyle: Google 7 | AccessModifierOffset: -1 8 | AlignAfterOpenBracket: Align 9 | AlignConsecutiveAssignments: None 10 | AlignOperands: Align 11 | AllowAllArgumentsOnNextLine: true 12 | AllowAllConstructorInitializersOnNextLine: true 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowShortBlocksOnASingleLine: Empty 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: Inline 17 | AllowShortIfStatementsOnASingleLine: Never # To avoid conflict, set this "Never" and each "if statement" should include brace when coding 18 | AllowShortLambdasOnASingleLine: Inline 19 | AllowShortLoopsOnASingleLine: false 20 | AlwaysBreakAfterReturnType: None 21 | AlwaysBreakTemplateDeclarations: Yes 22 | BinPackArguments: true 23 | BreakBeforeBraces: Custom 24 | BraceWrapping: 25 | AfterCaseLabel: false 26 | AfterClass: false 27 | AfterStruct: false 28 | AfterControlStatement: Never 29 | AfterEnum: false 30 | AfterFunction: false 31 | AfterNamespace: false 32 | AfterUnion: false 33 | AfterExternBlock: false 34 | BeforeCatch: false 35 | BeforeElse: false 36 | BeforeLambdaBody: false 37 | IndentBraces: false 38 | SplitEmptyFunction: false 39 | SplitEmptyRecord: false 40 | SplitEmptyNamespace: false 41 | BreakBeforeBinaryOperators: None 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializers: BeforeColon 44 | BreakInheritanceList: BeforeColon 45 | ColumnLimit: 80 46 | CompactNamespaces: false 47 | ContinuationIndentWidth: 4 48 | Cpp11BracedListStyle: true 49 | DerivePointerAlignment: false # Make sure the * or & align on the left 50 | EmptyLineBeforeAccessModifier: LogicalBlock 51 | FixNamespaceComments: true 52 | IncludeBlocks: Preserve 53 | IndentCaseLabels: true 54 | IndentPPDirectives: None 55 | IndentWidth: 2 56 | KeepEmptyLinesAtTheStartOfBlocks: true 57 | MaxEmptyLinesToKeep: 1 58 | NamespaceIndentation: None 59 | ObjCSpaceAfterProperty: false 60 | ObjCSpaceBeforeProtocolList: true 61 | PointerAlignment: Left 62 | ReflowComments: false 63 | # SeparateDefinitionBlocks: Always # Only support since clang-format 14 64 | SpaceAfterCStyleCast: false 65 | SpaceAfterLogicalNot: false 66 | SpaceAfterTemplateKeyword: true 67 | SpaceBeforeAssignmentOperators: true 68 | SpaceBeforeCpp11BracedList: false 69 | SpaceBeforeCtorInitializerColon: true 70 | SpaceBeforeInheritanceColon: true 71 | SpaceBeforeParens: ControlStatements 72 | SpaceBeforeRangeBasedForLoopColon: true 73 | SpaceBeforeSquareBrackets: false 74 | SpaceInEmptyParentheses: false 75 | SpacesBeforeTrailingComments: 2 76 | SpacesInAngles: false 77 | SpacesInCStyleCastParentheses: false 78 | SpacesInContainerLiterals: false 79 | SpacesInParentheses: false 80 | SpacesInSquareBrackets: false 81 | Standard: c++11 82 | TabWidth: 4 83 | UseTab: Never 84 | -------------------------------------------------------------------------------- /cmake/StandardSettings.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Project settings 3 | # 4 | 5 | option(${PROJECT_NAME}_BUILD_EXECUTABLE "Build the project as an executable, rather than a library." ON) 6 | option(${PROJECT_NAME}_BUILD_HEADERS_ONLY "Build the project as a header-only library." OFF) 7 | option(${PROJECT_NAME}_USE_ALT_NAMES "Use alternative names for the project, such as naming the include directory all lowercase." ON) 8 | 9 | # 10 | # Compiler options 11 | # 12 | 13 | option(${PROJECT_NAME}_WARNINGS_AS_ERRORS "Treat compiler warnings as errors." OFF) 14 | 15 | # 16 | # Package managers 17 | # 18 | # Currently supporting: Conan, Vcpkg. 19 | 20 | option(${PROJECT_NAME}_ENABLE_CONAN "Enable the Conan package manager for this project." OFF) 21 | option(${PROJECT_NAME}_ENABLE_VCPKG "Enable the Vcpkg package manager for this project." OFF) 22 | 23 | # 24 | # Unit testing 25 | # 26 | # Currently supporting: GoogleTest, Catch2. 27 | 28 | option(${PROJECT_NAME}_ENABLE_UNIT_TESTING "Enable unit tests for the projects (from the `test` subfolder)." ON) 29 | 30 | option(${PROJECT_NAME}_USE_GTEST "Use the GoogleTest project for creating unit tests." ON) 31 | option(${PROJECT_NAME}_USE_GOOGLE_MOCK "Use the GoogleMock project for extending the unit tests." OFF) 32 | 33 | option(${PROJECT_NAME}_USE_CATCH2 "Use the Catch2 project for creating unit tests." OFF) 34 | 35 | # 36 | # Static analyzers 37 | # 38 | # Currently supporting: Clang-Tidy, Cppcheck. 39 | 40 | option(${PROJECT_NAME}_ENABLE_CLANG_TIDY "Enable static analysis with Clang-Tidy." OFF) 41 | option(${PROJECT_NAME}_ENABLE_CPPCHECK "Enable static analysis with Cppcheck." OFF) 42 | 43 | # 44 | # Code coverage 45 | # 46 | 47 | option(${PROJECT_NAME}_ENABLE_CODE_COVERAGE "Enable code coverage through GCC." OFF) 48 | 49 | # 50 | # Doxygen 51 | # 52 | 53 | option(${PROJECT_NAME}_ENABLE_DOXYGEN "Enable Doxygen documentation builds of source." OFF) 54 | 55 | # 56 | # Miscelanious options 57 | # 58 | 59 | # Generate compile_commands.json for clang based tools 60 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 61 | 62 | option(${PROJECT_NAME}_VERBOSE_OUTPUT "Enable verbose output, allowing for a better understanding of each step taken." ON) 63 | option(${PROJECT_NAME}_GENERATE_EXPORT_HEADER "Create a `project_export.h` file containing all exported symbols." OFF) 64 | 65 | # Export all symbols when building a shared library 66 | if(BUILD_SHARED_LIBS) 67 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF) 68 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 69 | set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) 70 | endif() 71 | 72 | option(${PROJECT_NAME}_ENABLE_LTO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)." OFF) 73 | if(${PROJECT_NAME}_ENABLE_LTO) 74 | include(CheckIPOSupported) 75 | check_ipo_supported(RESULT result OUTPUT output) 76 | if(result) 77 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) 78 | else() 79 | message(SEND_ERROR "IPO is not supported: ${output}.") 80 | endif() 81 | endif() 82 | 83 | 84 | option(${PROJECT_NAME}_ENABLE_CCACHE "Enable the usage of Ccache, in order to speed up rebuild times." ON) 85 | find_program(CCACHE_FOUND ccache) 86 | if(CCACHE_FOUND) 87 | set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) 88 | set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) 89 | endif() 90 | 91 | option(${PROJECT_NAME}_ENABLE_ASAN "Enable Address Sanitize to detect memory error." OFF) 92 | if(${PROJECT_NAME}_ENABLE_ASAN) 93 | add_compile_options(-fsanitize=address) 94 | add_link_options(-fsanitize=address) 95 | endif() 96 | -------------------------------------------------------------------------------- /cmake/CompilerWarnings.cmake: -------------------------------------------------------------------------------- 1 | # from here: 2 | # 3 | # https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Avai 4 | # lable.md 5 | # Courtesy of Jason Turner 6 | 7 | function(set_project_warnings project_name) 8 | set(MSVC_WARNINGS 9 | /W4 # Baseline reasonable warnings 10 | /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss 11 | # of data 12 | /w14254 # 'operator': conversion from 'type1:field_bits' to 13 | # 'type2:field_bits', possible loss of data 14 | /w14263 # 'function': member function does not override any base class 15 | # virtual member function 16 | /w14265 # 'classname': class has virtual functions, but destructor is not 17 | # virtual instances of this class may not be destructed correctly 18 | /w14287 # 'operator': unsigned/negative constant mismatch 19 | /we4289 # nonstandard extension used: 'variable': loop control variable 20 | # declared in the for-loop is used outside the for-loop scope 21 | /w14296 # 'operator': expression is always 'boolean_value' 22 | /w14311 # 'variable': pointer truncation from 'type1' to 'type2' 23 | /w14545 # expression before comma evaluates to a function which is missing 24 | # an argument list 25 | /w14546 # function call before comma missing argument list 26 | /w14547 # 'operator': operator before comma has no effect; expected 27 | # operator with side-effect 28 | /w14549 # 'operator': operator before comma has no effect; did you intend 29 | # 'operator'? 30 | /w14555 # expression has no effect; expected expression with side- effect 31 | /w14619 # pragma warning: there is no warning number 'number' 32 | /w14640 # Enable warning on thread un-safe static member initialization 33 | /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may 34 | # cause unexpected runtime behavior. 35 | /w14905 # wide string literal cast to 'LPSTR' 36 | /w14906 # string literal cast to 'LPWSTR' 37 | /w14928 # illegal copy-initialization; more than one user-defined 38 | # conversion has been implicitly applied 39 | /permissive- # standards conformance mode for MSVC compiler. 40 | ) 41 | 42 | set(CLANG_WARNINGS 43 | -Wall 44 | -Wextra # reasonable and standard 45 | -Wshadow # warn the user if a variable declaration shadows one from a 46 | # parent context 47 | -Wnon-virtual-dtor # warn the user if a class with virtual functions has a 48 | # non-virtual destructor. This helps catch hard to 49 | # track down memory errors 50 | -Wold-style-cast # warn for c-style casts 51 | -Wcast-align # warn for potential performance problem casts 52 | -Wunused # warn on anything being unused 53 | -Woverloaded-virtual # warn if you overload (not override) a virtual 54 | # function 55 | -Wpedantic # warn if non-standard C++ is used 56 | -Wconversion # warn on type conversions that may lose data 57 | -Wsign-conversion # warn on sign conversions 58 | -Wnull-dereference # warn if a null dereference is detected 59 | -Wdouble-promotion # warn if float is implicit promoted to double 60 | -Wformat=2 # warn on security issues around functions that format output 61 | # (ie printf) 62 | ) 63 | 64 | if (${PROJECT_NAME}_WARNINGS_AS_ERRORS) 65 | set(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror) 66 | set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX) 67 | endif() 68 | 69 | set(GCC_WARNINGS 70 | ${CLANG_WARNINGS} 71 | -Wmisleading-indentation # warn if indentation implies blocks where blocks 72 | # do not exist 73 | -Wduplicated-cond # warn if if / else chain has duplicated conditions 74 | -Wduplicated-branches # warn if if / else branches have duplicated code 75 | -Wlogical-op # warn about logical operations being used where bitwise were 76 | # probably wanted 77 | -Wuseless-cast # warn if you perform a cast to the same type 78 | ) 79 | 80 | if(MSVC) 81 | set(PROJECT_WARNINGS ${MSVC_WARNINGS}) 82 | elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 83 | set(PROJECT_WARNINGS ${CLANG_WARNINGS}) 84 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 85 | set(PROJECT_WARNINGS ${GCC_WARNINGS}) 86 | else() 87 | message(AUTHOR_WARNING "No compiler warnings set for '${CMAKE_CXX_COMPILER_ID}' compiler.") 88 | endif() 89 | 90 | if(${PROJECT_NAME}_BUILD_HEADERS_ONLY) 91 | target_compile_options(${project_name} INTERFACE ${PROJECT_WARNINGS}) 92 | else() 93 | target_compile_options(${project_name} PUBLIC ${PROJECT_WARNINGS}) 94 | endif() 95 | 96 | if(NOT TARGET ${project_name}) 97 | message(AUTHOR_WARNING "${project_name} is not a target, thus no compiler warning were added.") 98 | endif() 99 | endfunction() 100 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | env: 9 | PROJECT_NAME: "CVM" 10 | BUILD_TYPE: Release 11 | 12 | jobs: 13 | build: 14 | name: ${{ matrix.config.name }} 15 | runs-on: ${{ matrix.config.os }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | config: 20 | - { 21 | name: "Windows Latest MSVC", 22 | artifact_ext: '.zip', 23 | os: windows-latest, 24 | cc: "cl", 25 | cxx: "cl", 26 | environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat", 27 | } 28 | - { 29 | name: "Ubuntu Latest GCC", 30 | artifact_ext: '.tar.gz', 31 | os: ubuntu-latest, 32 | cc: "gcc", 33 | cxx: "g++", 34 | } 35 | - { 36 | name: "macOS Latest Clang", 37 | artifact_ext: '.tar.gz', 38 | os: macos-latest, 39 | cc: "clang", 40 | cxx: "clang++", 41 | } 42 | 43 | steps: 44 | - name: set version name (Windows) 45 | id: version_win 46 | if: ${{ runner.os == 'Windows' }} 47 | run: | 48 | $TAG = (${env:GITHUB_REF} -replace 'refs/tags/', '') 49 | echo "::set-output name=name::$TAG" 50 | 51 | - name: set version name 52 | id: version 53 | if: ${{ runner.os != 'Windows' }} 54 | run: echo ::set-output name=name::${GITHUB_REF#refs/tags/} 55 | 56 | - name: Checkout 57 | uses: actions/checkout@v2 58 | with: 59 | submodules: recursive 60 | 61 | - name: cache dependencies 62 | uses: actions/cache@v2 63 | id: cache 64 | with: 65 | path: ${{ github.HOME }}/.local 66 | key: ${{ runner.os }}-dependencies 67 | 68 | - name: install GoogleTest 69 | if: ${{ steps.cache.output.cache-hit != 'true' }} 70 | run: | 71 | cd .. 72 | git clone https://github.com/google/googletest.git --branch main 73 | cd googletest 74 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX="$HOME/.local" -Dgtest_force_shared_crt=1 75 | cmake --build build --config Release 76 | cmake --build build --target install --config Release 77 | cd ../CVM 78 | 79 | - name: configure 80 | run: cmake -Bbuild -DCMAKE_INSTALL_PREFIX="$HOME/.local" 81 | 82 | - name: build 83 | run: cmake --build build --config "$env:BUILD_TYPE" -j4 84 | 85 | - name: run tests 86 | run: | 87 | cd build 88 | ctest -C "$env:BUILD_TYPE" -VV 89 | 90 | # for a release not containing directly the source code, replace the files archived 91 | # with the actual files needed (i.e. *.lib/*.a and *.h(pp)) 92 | 93 | - name: generate archive (Windows) 94 | if: ${{ runner.os == 'Windows' }} 95 | run: | 96 | rmdir -r -fo build 97 | 7z a -tzip $HOME/artifact.zip * 98 | 99 | 100 | - name: generate archive 101 | if: ${{ runner.os != 'Windows' }} 102 | run: | 103 | rm -rf build 104 | tar -cvzf $HOME/artifact.tar.gz . 105 | 106 | - name: upload artifacts 107 | uses: actions/upload-artifact@v2 108 | if: ${{ runner.os == 'Windows' }} 109 | with: 110 | name: ${{ runner.os }}-${{ steps.version_win.outputs.name }} 111 | path: '~/artifact.*' 112 | 113 | - name: upload artifacts 114 | uses: actions/upload-artifact@v2 115 | if: ${{ runner.os != 'Windows' }} 116 | with: 117 | name: ${{ runner.os }}-${{ steps.version.outputs.name }} 118 | path: '~/artifact.*' 119 | 120 | release: 121 | if: contains(github.ref, 'tags/v') 122 | runs-on: ubuntu-latest 123 | needs: build 124 | 125 | steps: 126 | - name: set version name 127 | id: version 128 | run: echo ::set-output name=name::${GITHUB_REF#refs/tags/} 129 | 130 | - name: create release 131 | id: create_release 132 | uses: actions/create-release@v1 133 | env: 134 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token 135 | with: 136 | tag_name: ${{ github.ref }} 137 | release_name: Release ${{ steps.version.outputs.name }} 138 | # if needed, you can set the release body here 139 | #body: "Release notes" 140 | draft: false 141 | prerelease: false 142 | 143 | - name: download artifact 144 | uses: actions/download-artifact@v2 145 | with: 146 | name: "Linux-${{ steps.version.outputs.name }}" 147 | path: ./ 148 | 149 | - name: upload ubuntu release asset 150 | uses: actions/upload-release-asset@v1 151 | env: 152 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 153 | with: 154 | upload_url: ${{ steps.create_release.outputs.upload_url }} 155 | asset_path: "artifact.tar.gz" 156 | asset_name: "${{ env.PROJECT_NAME }}-Linux-${{ steps.version.outputs.name }}.tar.gz" 157 | asset_content_type: application/x-tar 158 | 159 | - name: download artifact 160 | uses: actions/download-artifact@v2 161 | with: 162 | name: "Windows-${{ steps.version.outputs.name }}" 163 | path: ./ 164 | 165 | - name: upload windows release asset 166 | uses: actions/upload-release-asset@v1 167 | env: 168 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 169 | with: 170 | upload_url: ${{ steps.create_release.outputs.upload_url }} 171 | asset_path: "artifact.zip" 172 | asset_name: "${{ env.PROJECT_NAME }}-Windows-${{ steps.version.outputs.name }}.zip" 173 | asset_content_type: application/zip 174 | 175 | - name: download artifact 176 | uses: actions/download-artifact@v2 177 | with: 178 | name: "macOS-${{ steps.version.outputs.name }}" 179 | path: ./ 180 | 181 | - name: upload macos release asset 182 | uses: actions/upload-release-asset@v1 183 | env: 184 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 185 | with: 186 | upload_url: ${{ steps.create_release.outputs.upload_url }} 187 | asset_path: "./artifact.tar.gz" 188 | asset_name: "${{ env.PROJECT_NAME }}-macOS-${{ steps.version.outputs.name }}.tar.gz" 189 | asset_content_type: application/x-tar 190 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Bb]uild/* 2 | [Dd]ocs/* 3 | CMakeCache.* 4 | CMakeFiles/* 5 | [Tt]esting/* 6 | 7 | ### VisualStudioCode ### 8 | .vscode/* 9 | .vscode/settings.json 10 | .vscode/tasks.json 11 | .vscode/launch.json 12 | .vscode/extensions.json 13 | 14 | ### VisualStudioCode Patch ### 15 | # Ignore all local history of files 16 | .history 17 | 18 | ### VisualStudio ### 19 | ## Ignore Visual Studio temporary files, build results, and 20 | ## files generated by popular Visual Studio add-ons. 21 | 22 | # User-specific files 23 | *.rsuser 24 | *.suo 25 | *.user 26 | *.userosscache 27 | *.sln.docstates 28 | 29 | # User-specific files (MonoDevelop/Xamarin Studio) 30 | *.userprefs 31 | 32 | # Mono auto generated files 33 | mono_crash.* 34 | 35 | # Build results 36 | [Dd]ebug/ 37 | [Dd]ebugPublic/ 38 | [Rr]elease/ 39 | [Rr]eleases/ 40 | x64/ 41 | x86/ 42 | [Aa][Rr][Mm]/ 43 | [Aa][Rr][Mm]64/ 44 | bld/ 45 | [Bb]in/ 46 | [Oo]bj/ 47 | [Ll]og/ 48 | 49 | # Visual Studio 2015/2017 cache/options directory 50 | .vs/ 51 | # Uncomment if you have tasks that create the project's static files in wwwroot 52 | #wwwroot/ 53 | 54 | # Visual Studio 2017 auto generated files 55 | Generated\ Files/ 56 | 57 | # MSTest test Results 58 | [Tt]est[Rr]esult*/ 59 | [Bb]uild[Ll]og.* 60 | 61 | # NUnit 62 | *.VisualState.xml 63 | TestResult.xml 64 | nunit-*.xml 65 | 66 | # Build Results of an ATL Project 67 | [Dd]ebugPS/ 68 | [Rr]eleasePS/ 69 | dlldata.c 70 | 71 | # Benchmark Results 72 | BenchmarkDotNet.Artifacts/ 73 | 74 | # .NET Core 75 | project.lock.json 76 | project.fragment.lock.json 77 | artifacts/ 78 | 79 | # StyleCop 80 | StyleCopReport.xml 81 | 82 | # Files built by Visual Studio 83 | *_i.c 84 | *_p.c 85 | *_h.h 86 | *.ilk 87 | *.obj 88 | *.iobj 89 | *.pch 90 | *.pdb 91 | *.ipdb 92 | *.pgc 93 | *.pgd 94 | *.rsp 95 | *.sbr 96 | *.tlb 97 | *.tli 98 | *.tlh 99 | *.tmp 100 | *.tmp_proj 101 | *_wpftmp.csproj 102 | *.log 103 | *.vspscc 104 | *.vssscc 105 | .builds 106 | *.pidb 107 | *.svclog 108 | *.scc 109 | 110 | # Chutzpah Test files 111 | _Chutzpah* 112 | 113 | # Visual C++ cache files 114 | ipch/ 115 | *.aps 116 | *.ncb 117 | *.opendb 118 | *.opensdf 119 | *.sdf 120 | *.cachefile 121 | *.VC.db 122 | *.VC.VC.opendb 123 | 124 | # Visual Studio profiler 125 | *.psess 126 | *.vsp 127 | *.vspx 128 | *.sap 129 | 130 | # Visual Studio Trace Files 131 | *.e2e 132 | 133 | # TFS 2012 Local Workspace 134 | $tf/ 135 | 136 | # Guidance Automation Toolkit 137 | *.gpState 138 | 139 | # ReSharper is a .NET coding add-in 140 | _ReSharper*/ 141 | *.[Rr]e[Ss]harper 142 | *.DotSettings.user 143 | 144 | # JustCode is a .NET coding add-in 145 | .JustCode 146 | 147 | # TeamCity is a build add-in 148 | _TeamCity* 149 | 150 | # DotCover is a Code Coverage Tool 151 | *.dotCover 152 | 153 | # AxoCover is a Code Coverage Tool 154 | .axoCover/* 155 | !.axoCover/settings.json 156 | 157 | # Visual Studio code coverage results 158 | *.coverage 159 | *.coveragexml 160 | 161 | # NCrunch 162 | _NCrunch_* 163 | .*crunch*.local.xml 164 | nCrunchTemp_* 165 | 166 | # MightyMoose 167 | *.mm.* 168 | AutoTest.Net/ 169 | 170 | # Web workbench (sass) 171 | .sass-cache/ 172 | 173 | # Installshield output folder 174 | [Ee]xpress/ 175 | 176 | # DocProject is a documentation generator add-in 177 | DocProject/buildhelp/ 178 | DocProject/Help/*.HxT 179 | DocProject/Help/*.HxC 180 | DocProject/Help/*.hhc 181 | DocProject/Help/*.hhk 182 | DocProject/Help/*.hhp 183 | DocProject/Help/Html2 184 | DocProject/Help/html 185 | 186 | # Click-Once directory 187 | publish/ 188 | 189 | # Publish Web Output 190 | *.[Pp]ublish.xml 191 | *.azurePubxml 192 | # Note: Comment the next line if you want to checkin your web deploy settings, 193 | # but database connection strings (with potential passwords) will be unencrypted 194 | *.pubxml 195 | *.publishproj 196 | 197 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 198 | # checkin your Azure Web App publish settings, but sensitive information contained 199 | # in these scripts will be unencrypted 200 | PublishScripts/ 201 | 202 | # NuGet Packages 203 | *.nupkg 204 | # NuGet Symbol Packages 205 | *.snupkg 206 | # The packages folder can be ignored because of Package Restore 207 | **/[Pp]ackages/* 208 | # except build/, which is used as an MSBuild target. 209 | !**/[Pp]ackages/build/ 210 | # Uncomment if necessary however generally it will be regenerated when needed 211 | #!**/[Pp]ackages/repositories.config 212 | # NuGet v3's project.json files produces more ignorable files 213 | *.nuget.props 214 | *.nuget.targets 215 | 216 | # Microsoft Azure Build Output 217 | csx/ 218 | *.build.csdef 219 | 220 | # Microsoft Azure Emulator 221 | ecf/ 222 | rcf/ 223 | 224 | # Windows Store app package directories and files 225 | AppPackages/ 226 | BundleArtifacts/ 227 | Package.StoreAssociation.xml 228 | _pkginfo.txt 229 | *.appx 230 | *.appxbundle 231 | *.appxupload 232 | 233 | # Visual Studio cache files 234 | # files ending in .cache can be ignored 235 | *.[Cc]ache 236 | # but keep track of directories ending in .cache 237 | !?*.[Cc]ache/ 238 | 239 | # Others 240 | ClientBin/ 241 | ~$* 242 | *~ 243 | *.dbmdl 244 | *.dbproj.schemaview 245 | *.jfm 246 | *.pfx 247 | *.publishsettings 248 | orleans.codegen.cs 249 | 250 | #KDevelop 251 | *.kdev4 252 | .kdev4/* 253 | 254 | # Including strong name files can present a security risk 255 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 256 | #*.snk 257 | 258 | # Since there are multiple workflows, uncomment next line to ignore bower_components 259 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 260 | #bower_components/ 261 | 262 | # RIA/Silverlight projects 263 | Generated_Code/ 264 | 265 | # Backup & report files from converting an old project file 266 | # to a newer Visual Studio version. Backup files are not needed, 267 | # because we have git ;-) 268 | _UpgradeReport_Files/ 269 | Backup*/ 270 | UpgradeLog*.XML 271 | UpgradeLog*.htm 272 | ServiceFabricBackup/ 273 | *.rptproj.bak 274 | 275 | # SQL Server files 276 | *.mdf 277 | *.ldf 278 | *.ndf 279 | 280 | # Business Intelligence projects 281 | *.rdl.data 282 | *.bim.layout 283 | *.bim_*.settings 284 | *.rptproj.rsuser 285 | *- [Bb]ackup.rdl 286 | *- [Bb]ackup ([0-9]).rdl 287 | *- [Bb]ackup ([0-9][0-9]).rdl 288 | 289 | # Microsoft Fakes 290 | FakesAssemblies/ 291 | 292 | # GhostDoc plugin setting file 293 | *.GhostDoc.xml 294 | 295 | # Node.js Tools for Visual Studio 296 | .ntvs_analysis.dat 297 | node_modules/ 298 | 299 | # Visual Studio 6 build log 300 | *.plg 301 | 302 | # Visual Studio 6 workspace options file 303 | *.opt 304 | 305 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 306 | *.vbw 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # BeatPulse healthcheck temp database 365 | healthchecksdb 366 | 367 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 368 | MigrationBackup/ 369 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to CVM 2 | 3 | The CVM team encourages community feedback and contributions. 4 | Thank you for your interest in making CVM better! There are several 5 | ways you can get involved. 6 | 7 | If you are looking for a good way to contribute to the project, please: 8 | 9 | * have a look at the [available issue templates](https://github.com/lvntky/CVM/issues/new/choose) 10 | and checkout the [examples of good first issues](https://github.com/lvntky/CVM/contribute) 11 | (or [click here](https://github.com/lvntky/CVM/labels/good%20first%20issue)). 12 | 13 | * look through the [issues that need help](https://github.com/lvntky/CVM/labels/help%20wanted). 14 | 15 | * take a look at a [Pull Request template](PULL_REQUEST_TEMPLATE.md) to get yourself 16 | started. 17 | 18 | ## Reporting issues and suggesting new features 19 | 20 | If you find that the project is not working properly, please file a report using 21 | the [Bug Report template](https://github.com/lvntky/CVM/issues/new?assignees=&labels=bug&template=bug_report.md&title=[BUG]). 22 | Should the template provided not suit your needs, feel free to make a 23 | [custom Bug Report](https://github.com/lvntky/CVM/issues/new/choose), 24 | but please label it accordingly. 25 | 26 | We are happy to hear your ideas for how to further improve CVM, 27 | ensuring it suits your needs. Check the [Issues](https://github.com/lvntky/CVM/issues) 28 | and see if others have submitted similar feedback. You can upvote existing feedback 29 | (using the thumbs up reaction/by commenting) or [submit a new suggestion](https://github.com/lvntky/CVM/labels/feature). 30 | 31 | We always look at upvoted items in [Issues](https://github.com/lvntky/CVM/issues) 32 | when we decide what to work on next. We read the comments and we look forward to 33 | hearing your input. 34 | 35 | ## Finding issues you can help with 36 | 37 | Looking for something to work on? 38 | Issues marked [`good first issue`](https://github.com/lvntky/CVM/labels/good%20first%20issue) 39 | are a good place to start. 40 | 41 | You can also check the [`help wanted`](https://github.com/lvntky/CVM/labels/help%20wanted) 42 | tag to find other issues to help with. If you're interested in working on a fix, 43 | leave a comment to let everyone know and to help avoid duplicated effort from others. 44 | 45 | ## Contributions we accept 46 | 47 | We highly appreciate any contributions that help us improve the end product, with 48 | a high emphasis being put on any bug fixes you can manage to create and direct 49 | improvements which address the top issues reported by Calculator users. Some general 50 | guidelines: 51 | 52 | ### DOs 53 | 54 | * **DO** create one pull request per Issue, and ensure that the Issue is linked 55 | in the pull request. You can follow the [Pull Request Template](PULL_REQUEST_TEMPLATE.md) 56 | for this. 57 | 58 | * **DO** follow our [Coding and Style](#style-guidelines) guidelines, and keep code 59 | changes as small as possible. 60 | 61 | * **DO** include corresponding tests whenever possible. 62 | 63 | * **DO** check for additional occurrences of the same problem in other parts of the 64 | codebase before submitting your PR. 65 | 66 | * **DO** link the issue you are addressing in the pull request. 67 | 68 | * **DO** write a good description for your pull request. More detail is better. 69 | Describe *why* the change is being made and *why* you have chosen a particular solution. 70 | Describe any manual testing you performed to validate your change. 71 | 72 | ### DO NOTs 73 | 74 | * **DO NOT** merge multiple changes into one PR unless they have the same root cause. 75 | * **DO NOT** merge directly into the master branch. 76 | 77 | > Submitting a pull request for an approved Issue is not a guarantee it will be approved. 78 | > The change must meet our high bar for code quality, architecture and performance. 79 | 80 | ## Making changes to the code 81 | 82 | ### Preparing your development environment 83 | 84 | To learn how to build the code and run tests, follow the instructions in the [README](README.md). 85 | 86 | ### Style guidelines 87 | 88 | The code in this project uses several different coding styles, depending on the 89 | age and history of the code. Please attempt to match the style of surrounding 90 | code as much as possible. In new components, prefer the patterns described in the 91 | [C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines). 92 | 93 | ### Code formatting 94 | 95 | ***Run clang-format*** 96 | 97 | Use the following commands from the project's root directory to run clang-format 98 | (must be installed on the host system). 99 | 100 | **1. Run the CMake target for `clang-format`:** 101 | 102 | ```bash 103 | cmake --build build --target clang-format 104 | ``` 105 | 106 | **2. Using clang-format:** 107 | 108 | ```bash 109 | # !!! clang-format does not run recursively in subdirectories !!! 110 | # for each .cpp file modified 111 | clang-format -i *.cpp 112 | 113 | # for each .h file modified 114 | clang-format -i *.h 115 | 116 | # for each .hpp file modified 117 | clang-format -i *.hpp 118 | ``` 119 | 120 | **3. Using TheLartians' Format.cmake:** 121 | 122 | ```bash 123 | cmake -Htest -Bbuild/test 124 | 125 | # view changes 126 | cmake --build build/test --target format 127 | 128 | # apply changes 129 | cmake --build build/test --target fix-format 130 | ``` 131 | 132 | See [Format.cmake](https://github.com/TheLartians/Format.cmake) for more options. 133 | 134 | ### Testing 135 | 136 | Your change should include tests to verify new functionality wherever possible. 137 | Code should be structured so that it can be unit tested independently of the UI. 138 | Manual test cases should be used where automated testing is not feasible. 139 | 140 | ### Git workflow 141 | 142 | The core principle of the project, when it comes to Git workflows is that the 143 | `master` branch should always be in a healthy state which is ready for release. 144 | Every commit on master should be deployable on push. To ensure this, pull request 145 | **must not** be made directly on master. **Each change** should either be made in 146 | the **development branch** (named a variation of development, i.e. `dev`) or in a 147 | separate branch, named as a short summary of the change. 148 | 149 | If your change is complex, please clean up the branch history before submitting a 150 | pull request. You can use [git rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) 151 | to group your changes into a small number of commits which we can review one at a 152 | time. 153 | 154 | When completing a pull request, we will generally squash your changes into a single 155 | commit. After confirming that the change works as intended, the branch *might* be 156 | deleted, in order to prevent branch polluting. Please let us know if your pull request 157 | needs to be merged as separate commits. 158 | 159 | ### Continuous Integration 160 | 161 | For this project, CI is provided by [GitHub Actions](https://github.com/features/actions), 162 | with workflows found in the [`.github/workflows` folder](.github/workflows). Workflows 163 | are run automatically on every commit made on the master branch, unless told to skip 164 | for that particular commit. 165 | 166 | To skip CI runs on a particular commit, include either `[skip ci]` or `[ci skip]` 167 | in the commit message. 168 | 169 | ```bash 170 | # an example of a commit message that would not trigger CI workflows 171 | git commit -m "my normal commit message [skip ci]" 172 | # or 173 | git commit -m "my normal commit message [ci skip]" 174 | ``` 175 | 176 | ## Review process 177 | 178 | After submitting a pull request, members of the team will review your code. We will 179 | assign the request to an appropriate reviewer (if applicable). Any member of the 180 | community may participate in the review, but at least one member of the project team 181 | will ultimately approve the request. 182 | 183 | Often, multiple iterations or discussions will be needed to responding to feedback 184 | from reviewers. Try looking at [past pull requests](https://github.com/lvntky/CVM/pulls?q=is%3Apr+is%3Aclosed) 185 | to see what the experience might be like. 186 | 187 | ## Contributor License Agreement 188 | 189 | Before we can review and accept a pull request from you, you'll need to sign a 190 | Contributor License Agreement (CLA). The CLA ensures that the community is free 191 | to use your contributions. Signing the CLA is a manual process, and you need to 192 | do it for each pull request made. This is done by checking the boxes in the 193 | [Pull Request Readiness Checklist of a Pull Request](PULL_REQUEST_TEMPLATE.md#Pull-Request-Readiness-Checklist). 194 | 195 | ### IMPORTANT 196 | 197 | ***Checking the aforementioned boxes means that you agree to provide your change 198 | and/or code FREE TO USE and SUBJECT TO CHANGES for the entire community!*** 199 | 200 | You don't need to sign a CLA until you're ready to create a pull request. When your 201 | pull request is created, it is reviewed by a team member which, if the change is 202 | trivial (i.e. you just fixed a typo) will be labelled as `cla-not-required`. 203 | Otherwise, it's classified as `cla-required`, if not already signed. 204 | -------------------------------------------------------------------------------- /src/execute_engine/cvm_execute.cpp: -------------------------------------------------------------------------------- 1 | #include "../../include/cvm/execute_engine/cvm_execute.hpp" 2 | 3 | #include 4 | 5 | // Temporarily store codeLength globally 6 | size_t codeLength = 0; 7 | size_t maxLocals = 0; 8 | 9 | /** 10 | * Helper methods 11 | * for validating execution and 12 | * interpreting 13 | */ 14 | 15 | void validateMethod(const method_info* methodInfo, std::string methodName) { 16 | if (methodInfo != nullptr) { 17 | spdlog::info("{} The given [method: {}] has been found on classfile", 18 | LOG_OK, methodName); 19 | } else { 20 | spdlog::error("{} The given [method: {}] cannot be foudn on classfile", 21 | LOG_NOK, methodName); 22 | throw std::runtime_error("Method not found"); 23 | } 24 | } 25 | 26 | void validateByteCode(const uint8_t* byteCode) { 27 | if (byteCode != nullptr) { 28 | spdlog::info("{} Bytcode is obtained from given method:", LOG_OK); 29 | } else { 30 | throw std::runtime_error("Could not get bytecode for given method"); 31 | } 32 | } 33 | 34 | std::string printOperandStack(std::vector osp) { 35 | std::string osStr = "["; 36 | for (size_t i = 0; i < osp.size(); i++) { 37 | osStr += std::to_string(osp.at(i)); 38 | if (i != osp.size() - 1) { 39 | osStr += ", "; 40 | } 41 | } 42 | osStr += "]"; 43 | return osStr; 44 | } 45 | 46 | void pushOperandStack(std::stack& operandStack, 47 | std::vector& operandStackPrint, int value) { 48 | operandStack.push(value); 49 | operandStackPrint.push_back(value); 50 | } 51 | 52 | int popOperandStack(std::stack& operandStack, 53 | std::vector& operandStackPrint) { 54 | int value = operandStack.top(); 55 | operandStack.pop(); 56 | 57 | operandStackPrint.pop_back(); 58 | 59 | return value; 60 | } 61 | /** 62 | * Helper methods ends 63 | */ 64 | 65 | void CVM::execute(const Classfile& cf, const std::string& methodName) { 66 | spdlog::info("CVM executing method: {} on parsed classfile.", methodName); 67 | const method_info* methodInfo = findMehodByName(cf, methodName); 68 | validateMethod(methodInfo, methodName); 69 | 70 | const uint8_t* bytecode = getByteCode(cf, methodInfo); 71 | validateByteCode(bytecode); 72 | 73 | interprete(bytecode, cf); 74 | } 75 | 76 | std::string CVM::getUtf8FromConstantPool(const Classfile& cf, uint16_t index) { 77 | const cp_info& cpEntry = cf.constant_pool.at(index - 1); 78 | if (cpEntry.tag == CONSTANT_Utf8) { 79 | return std::string(cpEntry.info.Utf8.bytes, 80 | cpEntry.info.Utf8.bytes + cpEntry.info.Utf8.length); 81 | } 82 | return ""; 83 | } 84 | 85 | const method_info* CVM::findMehodByName(const Classfile& cf, 86 | const std::string& methodName) { 87 | for (auto& method : cf.methods) { 88 | std::string name = getUtf8FromConstantPool(cf, method.name_index); 89 | if (name == methodName) { 90 | return &method; 91 | } 92 | } 93 | return nullptr; 94 | } 95 | 96 | const uint8_t* CVM::getByteCode(const Classfile& cf, 97 | const method_info* methodInfo) { 98 | for (auto& attr : methodInfo->attributes) { 99 | std::string codeAttribute = 100 | getUtf8FromConstantPool(cf, attr.attribute_name_index); 101 | if (codeAttribute == "Code") { 102 | codeLength = attr.attribute_length; 103 | spdlog::info("code length: {}, The attribute name index: {:#x}", 104 | codeLength, attr.attribute_name_index); 105 | maxLocals = (attr.info[2] << 8) | 106 | attr.info[3]; // max_locals is a 2-byte value at offset 2 107 | spdlog::info("maxLocals = {}", maxLocals); 108 | return attr.info; 109 | } 110 | } 111 | return nullptr; 112 | } 113 | 114 | void CVM::interprete(const uint8_t* byteCode, const Classfile& cf) { 115 | size_t pc = 0; 116 | std::stack operandStack; 117 | std::vector 118 | operandStackPrint; // for printing elements of original operandStack 119 | std::vector localIntVariables(maxLocals); 120 | 121 | while (pc < codeLength) { 122 | uint8_t opcode = byteCode[pc++]; 123 | int operand = 0; 124 | int valueToStore = 0; 125 | 126 | // values for 0x60 127 | int val_1 = 0; 128 | int val_2 = 0; 129 | 130 | switch (opcode) { 131 | case 0x0: 132 | spdlog::info("==> Opcode: {:#X} - NOP - DO NOTHING", opcode); 133 | break; 134 | case 0x2: 135 | pushOperandStack(operandStack, operandStackPrint, -1); 136 | spdlog::info( 137 | "==> Opcode {:#X} - iconst_m1 - Load m1 to the operand stack: {}", 138 | opcode, printOperandStack(operandStackPrint)); 139 | break; 140 | case 0x3: 141 | pushOperandStack(operandStack, operandStackPrint, 0); 142 | 143 | spdlog::info( 144 | "==> Opcode {:#X} - iconst_0 - Load 0 to the operand stack: {}", 145 | opcode, printOperandStack(operandStackPrint)); 146 | break; 147 | case 0x4: 148 | pushOperandStack(operandStack, operandStackPrint, 1); 149 | spdlog::info( 150 | "==> Opcode {:#X} - iconst_1 - Load 1 to the operand stack: {}", 151 | opcode, printOperandStack(operandStackPrint)); 152 | break; 153 | case 0x10: 154 | operand = byteCode[pc++]; 155 | pushOperandStack(operandStack, operandStackPrint, operand); 156 | spdlog::info( 157 | "==> Opcode {:#X} - bipush - The immediate byte is sign-extended " 158 | "to an int value. That value is pushed onto the operand stack. {}", 159 | opcode, printOperandStack(operandStackPrint)); 160 | break; 161 | case 0x3c: 162 | valueToStore = popOperandStack(operandStack, operandStackPrint); 163 | localIntVariables.at(1) = valueToStore; 164 | spdlog::info( 165 | "==> Opcode {:#X} - istore_1 - The value on the top of the operand " 166 | "stack must be of type int. It is popped from the operand stack, " 167 | "and the value of the local variable at #1 index is set to value. " 168 | "Operand stack: {}", 169 | opcode, printOperandStack(operandStackPrint)); 170 | break; 171 | case 0x3d: 172 | valueToStore = popOperandStack(operandStack, operandStackPrint); 173 | localIntVariables.at(2) = valueToStore; 174 | spdlog::info( 175 | "==> Opcode {:#X} - istore_2 - The value on the top of the operand " 176 | "stack must be of type int. It is popped from the operand stack, " 177 | "and the value of the local variable at #2 is set to value. " 178 | "Operand stack: {}", 179 | opcode, printOperandStack(operandStackPrint)); 180 | break; 181 | case 0x3e: 182 | valueToStore = popOperandStack(operandStack, operandStackPrint); 183 | localIntVariables.at(3) = valueToStore; 184 | spdlog::info( 185 | "==> Opcode {:#X} - istore_3 - The value on the top of the operand " 186 | "stack must be of type int. It is popped from the operand stack, " 187 | "and the value of the local variable at #3 is set to value. " 188 | "Operand stack: {}", 189 | opcode, printOperandStack(operandStackPrint)); 190 | break; 191 | case 0x1b: 192 | pushOperandStack(operandStack, operandStackPrint, 193 | localIntVariables.at(1)); 194 | spdlog::info( 195 | "==> Opcode {:#X} - iload_1 - The value of the local variable at " 196 | "#1 is pushed onto the operand stack: {}", 197 | opcode, printOperandStack(operandStackPrint)); 198 | break; 199 | case 0x1c: 200 | pushOperandStack(operandStack, operandStackPrint, 201 | localIntVariables.at(2)); 202 | spdlog::info( 203 | "==> Opcode {:#X} - iload_2 - The value of the local variable at " 204 | "#2 is pushed onto the operand stack: {}", 205 | opcode, printOperandStack(operandStackPrint)); 206 | break; 207 | case 0x1d: 208 | pushOperandStack(operandStack, operandStackPrint, 209 | localIntVariables.at(3)); 210 | spdlog::info( 211 | "==> Opcode {:#X} - iload_3 - The value of the local variable at " 212 | "#3 is pushed onto the operand stack: {}", 213 | opcode, printOperandStack(operandStackPrint)); 214 | break; 215 | case 0x60: 216 | val_1 = popOperandStack(operandStack, operandStackPrint); 217 | val_2 = popOperandStack(operandStack, operandStackPrint); 218 | pushOperandStack(operandStack, operandStackPrint, val_1 + val_2); 219 | spdlog::info( 220 | "==> Opcode {:#X} - iadd - Get last 2 variables on the satck, add " 221 | "them together push operand stack: {}", 222 | opcode, printOperandStack(operandStackPrint)); 223 | break; 224 | case 0xac: 225 | return_value = 226 | std::to_string(popOperandStack(operandStack, operandStackPrint)); 227 | spdlog::info( 228 | "==> Opcode {:#X} - iret - Return[val = {}] the top operand " 229 | "stack: {}", 230 | opcode, return_value, printOperandStack(operandStackPrint)); 231 | break; 232 | default: 233 | spdlog::error("Unknown Opcode: {:#x} at PC: {}", opcode, pc); 234 | break; 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | # 4 | # Project details 5 | # 6 | 7 | project( 8 | CVM 9 | VERSION 2.0.0 10 | LANGUAGES CXX 11 | ) 12 | 13 | # 14 | # Set project options 15 | # 16 | 17 | include(cmake/StandardSettings.cmake) 18 | include(cmake/Utils.cmake) 19 | if(NOT CMAKE_BUILD_TYPE) 20 | set(CMAKE_BUILD_TYPE "Debug") 21 | endif() 22 | message(STATUS "Started CMake for ${PROJECT_NAME} v${PROJECT_VERSION}...\n") 23 | 24 | if (UNIX) 25 | add_compile_options("$<$:-D_DEBUG>") #this will allow to use same _DEBUG macro available in both Linux as well as Windows - MSCV environment. Easy to put Debug specific code. 26 | endif (UNIX) 27 | 28 | 29 | # 30 | # Setup alternative names 31 | # 32 | 33 | if(${PROJECT_NAME}_USE_ALT_NAMES) 34 | string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE) 35 | string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPERCASE) 36 | else() 37 | set(PROJECT_NAME_LOWERCASE ${PROJECT_NAME}) 38 | set(PROJECT_NAME_UPPERCASE ${PROJECT_NAME}) 39 | endif() 40 | 41 | # 42 | # Prevent building in the source directory 43 | # 44 | 45 | if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) 46 | message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.\n") 47 | endif() 48 | 49 | 50 | # 51 | # Create library, setup header and source files 52 | # 53 | 54 | # Find all headers and implementation files 55 | include(cmake/SourcesAndHeaders.cmake) 56 | 57 | if(${PROJECT_NAME}_BUILD_EXECUTABLE) 58 | add_executable(${PROJECT_NAME} ${exe_sources}) 59 | 60 | if(${PROJECT_NAME}_VERBOSE_OUTPUT) 61 | verbose_message("Found the following sources:") 62 | foreach(source IN LISTS exe_sources) 63 | verbose_message("* ${source}") 64 | endforeach() 65 | endif() 66 | 67 | if(${PROJECT_NAME}_ENABLE_UNIT_TESTING) 68 | add_library(${PROJECT_NAME}_LIB ${headers} ${sources}) 69 | 70 | if(${PROJECT_NAME}_VERBOSE_OUTPUT) 71 | verbose_message("Found the following headers:") 72 | foreach(header IN LISTS headers) 73 | verbose_message("* ${header}") 74 | endforeach() 75 | endif() 76 | endif() 77 | elseif(${PROJECT_NAME}_BUILD_HEADERS_ONLY) 78 | add_library(${PROJECT_NAME} INTERFACE) 79 | 80 | if(${PROJECT_NAME}_VERBOSE_OUTPUT) 81 | verbose_message("Found the following headers:") 82 | foreach(header IN LIST headers) 83 | verbose_message("* ${header}") 84 | endforeach() 85 | endif() 86 | else() 87 | add_library( 88 | ${PROJECT_NAME} 89 | ${headers} 90 | ${sources} 91 | ) 92 | 93 | if(${PROJECT_NAME}_VERBOSE_OUTPUT) 94 | verbose_message("Found the following sources:") 95 | foreach(source IN LISTS sources) 96 | verbose_message("* ${source}") 97 | endforeach() 98 | verbose_message("Found the following headers:") 99 | foreach(header IN LISTS headers) 100 | verbose_message("* ${header}") 101 | endforeach() 102 | endif() 103 | endif() 104 | 105 | set_target_properties( 106 | ${PROJECT_NAME} 107 | PROPERTIES 108 | ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}" 109 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}" 110 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE}" 111 | ) 112 | if(${PROJECT_NAME}_BUILD_EXECUTABLE AND ${PROJECT_NAME}_ENABLE_UNIT_TESTING) 113 | set_target_properties( 114 | ${PROJECT_NAME}_LIB 115 | PROPERTIES 116 | ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}" 117 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}" 118 | OUTPUT_NAME ${PROJECT_NAME} 119 | ) 120 | endif() 121 | 122 | message(STATUS "Added all header and implementation files.\n") 123 | 124 | # 125 | # Set the project standard and warnings 126 | # 127 | 128 | if(${PROJECT_NAME}_BUILD_HEADERS_ONLY) 129 | target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_17) 130 | else() 131 | target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) 132 | 133 | if(${PROJECT_NAME}_BUILD_EXECUTABLE AND ${PROJECT_NAME}_ENABLE_UNIT_TESTING) 134 | target_compile_features(${PROJECT_NAME}_LIB PUBLIC cxx_std_17) 135 | endif() 136 | endif() 137 | include(cmake/CompilerWarnings.cmake) 138 | set_project_warnings(${PROJECT_NAME}) 139 | 140 | verbose_message("Applied compiler warnings. Using standard ${CMAKE_CXX_STANDARD}.\n") 141 | 142 | # 143 | # Model project dependencies 144 | # 145 | 146 | # Identify and link with the specific "packages" the project uses 147 | #find_package(package_name package_version REQUIRED package_type [other_options]) 148 | #target_link_libraries( 149 | # ${PROJECT_NAME} 150 | # PUBLIC 151 | # dependency1 ... 152 | # PRIVATE 153 | # dependency2 ... 154 | # ${PROJECT_NAME}_PROJECT_OPTIONS 155 | # ${PROJECT_NAME}_PROJECT_WARNINGS 156 | #) 157 | #if(${PROJECT_NAME}_BUILD_EXECUTABLE AND ${PROJECT_NAME}_ENABLE_UNIT_TESTING) 158 | # target_link_libraries( 159 | # ${PROJECT_NAME}_LIB 160 | # PUBLIC 161 | # dependency1 ... 162 | # ) 163 | #endif() 164 | find_package(fmt REQUIRED) 165 | 166 | target_link_libraries(${PROJECT_NAME} fmt::fmt) 167 | 168 | # For Windows, it is necessary to link with the MultiThreaded library. 169 | # Depending on how the rest of the project's dependencies are linked, it might be necessary 170 | # to change the line to statically link with the library. 171 | # 172 | # This is done as follows: 173 | # 174 | # set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 175 | # 176 | # On Linux and Mac this variable is ignored. If any issues rise from it, try commenting it out 177 | # and letting CMake decide how to link with it. 178 | set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") 179 | 180 | verbose_message("Successfully added all dependencies and linked against them.") 181 | 182 | # 183 | # Set the build/user include directories 184 | # 185 | 186 | # Allow usage of header files in the `src` directory, but only for utilities 187 | if(${PROJECT_NAME}_BUILD_HEADERS_ONLY) 188 | target_include_directories( 189 | ${PROJECT_NAME} 190 | INTERFACE 191 | $ 192 | $ 193 | ) 194 | else() 195 | target_include_directories( 196 | ${PROJECT_NAME} 197 | PUBLIC 198 | $ 199 | $ 200 | PRIVATE 201 | ${CMAKE_CURRENT_SOURCE_DIR}/src 202 | ) 203 | if(${PROJECT_NAME}_BUILD_EXECUTABLE AND ${PROJECT_NAME}_ENABLE_UNIT_TESTING) 204 | target_include_directories( 205 | ${PROJECT_NAME}_LIB 206 | PUBLIC 207 | $ 208 | $ 209 | PRIVATE 210 | ${CMAKE_CURRENT_SOURCE_DIR}/src 211 | ) 212 | endif() 213 | endif() 214 | 215 | message(STATUS "Finished setting up include directories.") 216 | 217 | # 218 | # Provide alias to library for 219 | # 220 | 221 | if(${PROJECT_NAME}_BUILD_EXECUTABLE) 222 | add_executable(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 223 | else() 224 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 225 | endif() 226 | 227 | verbose_message("Project is now aliased as ${PROJECT_NAME}::${PROJECT_NAME}.\n") 228 | 229 | # 230 | # Format the project using the `clang-format` target (i.e: cmake --build build --target clang-format) 231 | # 232 | 233 | add_clang_format_target() 234 | 235 | # 236 | # Install library for easy downstream inclusion 237 | # 238 | 239 | include(GNUInstallDirs) 240 | install( 241 | TARGETS 242 | ${PROJECT_NAME} 243 | EXPORT 244 | ${PROJECT_NAME}Targets 245 | LIBRARY DESTINATION 246 | ${CMAKE_INSTALL_LIBDIR} 247 | RUNTIME DESTINATION 248 | ${CMAKE_INSTALL_BINDIR} 249 | ARCHIVE DESTINATION 250 | ${CMAKE_INSTALL_LIBDIR} 251 | INCLUDES DESTINATION 252 | include 253 | PUBLIC_HEADER DESTINATION 254 | include 255 | ) 256 | 257 | install( 258 | EXPORT 259 | ${PROJECT_NAME}Targets 260 | FILE 261 | ${PROJECT_NAME}Targets.cmake 262 | NAMESPACE 263 | ${PROJECT_NAME}:: 264 | DESTINATION 265 | ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 266 | ) 267 | 268 | # 269 | # Add version header 270 | # 271 | 272 | configure_file( 273 | ${CMAKE_CURRENT_LIST_DIR}/cmake/version.hpp.in 274 | include/${PROJECT_NAME_LOWERCASE}/version.hpp 275 | @ONLY 276 | ) 277 | 278 | install( 279 | FILES 280 | ${CMAKE_CURRENT_BINARY_DIR}/include/${PROJECT_NAME_LOWERCASE}/version.hpp 281 | DESTINATION 282 | include/${PROJECT_NAME_LOWERCASE} 283 | ) 284 | 285 | # 286 | # Install the `include` directory 287 | # 288 | 289 | install( 290 | DIRECTORY 291 | include/${PROJECT_NAME_LOWERCASE} 292 | DESTINATION 293 | include 294 | ) 295 | 296 | verbose_message("Install targets successfully built. Install with `cmake --build --target install --config `.") 297 | 298 | # 299 | # Quick `ConfigVersion.cmake` creation 300 | # 301 | 302 | include(CMakePackageConfigHelpers) 303 | write_basic_package_version_file( 304 | ${PROJECT_NAME}ConfigVersion.cmake 305 | VERSION 306 | ${PROJECT_VERSION} 307 | COMPATIBILITY 308 | SameMajorVersion 309 | ) 310 | 311 | configure_package_config_file( 312 | ${CMAKE_CURRENT_LIST_DIR}/cmake/${PROJECT_NAME}Config.cmake.in 313 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 314 | INSTALL_DESTINATION 315 | ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 316 | ) 317 | 318 | install( 319 | FILES 320 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 321 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 322 | DESTINATION 323 | ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 324 | ) 325 | 326 | # 327 | # Generate export header if specified 328 | # 329 | 330 | if(${PROJECT_NAME}_GENERATE_EXPORT_HEADER) 331 | include(GenerateExportHeader) 332 | generate_export_header(${PROJECT_NAME}) 333 | install( 334 | FILES 335 | ${PROJECT_BINARY_DIR}/${PROJECT_NAME_LOWERCASE}_export.h 336 | DESTINATION 337 | include 338 | ) 339 | 340 | message(STATUS "Generated the export header `${PROJECT_NAME_LOWERCASE}_export.h` and installed it.") 341 | endif() 342 | 343 | message(STATUS "Finished building requirements for installing the package.\n") 344 | 345 | # 346 | # Unit testing setup 347 | # 348 | 349 | if(${PROJECT_NAME}_ENABLE_UNIT_TESTING) 350 | enable_testing() 351 | message(STATUS "Build unit tests for the project. Tests should always be found in the test folder\n") 352 | add_subdirectory(test) 353 | endif() 354 | -------------------------------------------------------------------------------- /src/classfile/classfile.cpp: -------------------------------------------------------------------------------- 1 | #include "../../include/cvm/classfile/classfile.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "../../include/cvm/log.hpp" 8 | 9 | std::string cp_info_to_string(const cp_info& cp) { 10 | switch (cp.tag) { 11 | case CONSTANT_Class: 12 | return fmt::format("Class: name_index = {}", cp.info.Class.name_index); 13 | case CONSTANT_String: 14 | return fmt::format("String: string_index = {}", 15 | cp.info.String.string_index); 16 | case CONSTANT_Fieldref: 17 | return fmt::format("Fieldref: class_index = {}, name_and_type_index = {}", 18 | cp.info.Fieldref.class_index, 19 | cp.info.Fieldref.name_and_type_index); 20 | case CONSTANT_Methodref: 21 | return fmt::format( 22 | "Methodref: class_index = {}, name_and_type_index = {}", 23 | cp.info.Methodref.class_index, cp.info.Methodref.name_and_type_index); 24 | case CONSTANT_InterfaceMethodref: 25 | return fmt::format( 26 | "InterfaceMethodref: class_index = {}, name_and_type_index = {}", 27 | cp.info.InterfaceMethodref.class_index, 28 | cp.info.InterfaceMethodref.name_and_type_index); 29 | case CONSTANT_Integer: 30 | return fmt::format("Integer: bytes = {}", cp.info.Integer.bytes); 31 | case CONSTANT_NameAndType: 32 | return fmt::format("NameAndType: name_index = {}, descriptor_index = {}", 33 | cp.info.NameAndType.name_index, 34 | cp.info.NameAndType.descriptor_index); 35 | case CONSTANT_Double: 36 | return fmt::format("Double: bytes = {}", cp.info.Double.bytes); 37 | case CONSTANT_Utf8: 38 | return fmt::format("Utf8: length = {}, bytes = {}", cp.info.Utf8.length, 39 | std::string(cp.info.Utf8.bytes, 40 | cp.info.Utf8.bytes + cp.info.Utf8.length)); 41 | default: 42 | return "Unknown cp_info tag"; 43 | } 44 | } 45 | 46 | uint16_t Classfile::readShort(const uint8_t* bytes, size_t& offset) { 47 | setLevel(); 48 | spdlog::debug("Reading short at offset: {}", offset); 49 | uint16_t value = (bytes[offset] << 8 | bytes[offset + 1]); 50 | offset += 2; 51 | spdlog::debug("Read short: 0x{:04x}, new offset: {}", value, offset); 52 | return value; 53 | } 54 | 55 | uint32_t Classfile::readInt(const uint8_t* bytes, size_t& offset) { 56 | setLevel(); 57 | spdlog::debug("Reading int at offset: {}", offset); 58 | uint32_t value = (bytes[offset] << 24) | (bytes[offset + 1] << 16) | 59 | (bytes[offset + 2] << 8) | bytes[offset + 3]; 60 | offset += 4; 61 | spdlog::debug("Read int: 0x{:08x}, new offset: {}", value, offset); 62 | return value; 63 | } 64 | 65 | uint64_t Classfile::readLong(const uint8_t* bytes, size_t& offset) { 66 | setLevel(); 67 | spdlog::debug("Reading long at offset: {}", offset); 68 | uint64_t value = (static_cast(bytes[offset]) << 56) | 69 | (static_cast(bytes[offset + 1]) << 48) | 70 | (static_cast(bytes[offset + 2]) << 40) | 71 | (static_cast(bytes[offset + 3]) << 32) | 72 | (static_cast(bytes[offset + 4]) << 24) | 73 | (static_cast(bytes[offset + 5]) << 16) | 74 | (static_cast(bytes[offset + 6]) << 8) | 75 | static_cast(bytes[offset + 7]); 76 | offset += 8; 77 | spdlog::debug("Read long: 0x{:016x}, new offset: {}", value, offset); 78 | return value; 79 | } 80 | 81 | double Classfile::readDouble(const uint8_t* bytes, size_t& offset) { 82 | setLevel(); 83 | spdlog::debug("Reading double at offset: {}", offset); 84 | uint64_t longValue = readLong(bytes, offset); 85 | double value; 86 | std::memcpy(&value, &longValue, sizeof(value)); 87 | spdlog::debug("Read double: {}, new offset: {}", value, offset); 88 | return value; 89 | } 90 | 91 | std::string Classfile::to_string() const { 92 | std::string cp_str; 93 | for (size_t i = 0; i < constant_pool.size(); ++i) { 94 | cp_str += fmt::format(" [{:2}] {}\n", i + 1, 95 | cp_info_to_string(constant_pool.at(i))); 96 | } 97 | 98 | std::string intf_str; 99 | for (const auto& intf : interfaces) { 100 | intf_str += fmt::format(" 0x{:04x}\n", intf); 101 | } 102 | 103 | std::string fields_str; 104 | for (const auto& field : fields) { 105 | fields_str += fmt::format(" {}\n", field.to_string()); 106 | } 107 | 108 | std::string methods_str; 109 | for (const auto& method : methods) { 110 | methods_str += fmt::format(" {}\n", method.to_string()); 111 | } 112 | 113 | std::string attributes_str; 114 | for (const auto& attr : attributes) { 115 | attributes_str += fmt::format(" {}\n", attr.to_string()); 116 | } 117 | 118 | return fmt::format( 119 | "Classfile Dump:\n" 120 | " Magic Number: 0x{:08x}\n" 121 | " Minor Version: {}\n" 122 | " Major Version: {}\n" 123 | " Constant Pool Count: {}\n" 124 | " Constant Pool:\n [\n{}\n ]\n" 125 | " Access Flags: 0x{:04x}\n" 126 | " This Class: 0x{:04x}\n" 127 | " Super Class: 0x{:04x}\n" 128 | " Interfaces Count: {}\n" 129 | " Interfaces:\n [\n{}\n ]\n" 130 | " Fields Count: {}\n" 131 | " Fields:\n [\n{}\n ]\n" 132 | " Methods Count: {}\n" 133 | " Methods:\n [\n{}\n ]\n" 134 | " Attributes Count: {}\n" 135 | " Attributes:\n [\n{}\n ]\n", 136 | magic, minor_version, major_version, constant_pool_count, cp_str, 137 | access_flags, this_class, super_class, interfaces_count, intf_str, 138 | fields_count, fields_str, methods_count, methods_str, attributes_count, 139 | attributes_str); 140 | } 141 | 142 | std::ostream& operator<<(std::ostream& os, const Classfile& cf) { 143 | return os << cf.to_string(); 144 | } 145 | /** 146 | * Helper methods for validating etc. 147 | */ 148 | bool validateMagicNumber(uint32_t magic) { 149 | return magic == 0xcafebabe; 150 | } 151 | 152 | void parseConstantPool(const uint8_t* bytes, size_t& offset, Classfile& cf) { 153 | setLevel(); 154 | cf.constant_pool.resize(cf.constant_pool_count - 1); 155 | spdlog::info("Constant pool size: {}", cf.constant_pool_count - 1); 156 | 157 | for (uint16_t i = 1; i < cf.constant_pool_count; i++) { 158 | cp_info& cp_entry = cf.constant_pool[i - 1]; 159 | cp_entry.tag = bytes[offset++]; 160 | 161 | spdlog::debug("Parsing constant pool entry {}, tag: {}, offset: {}", i, 162 | cp_entry.tag, offset); 163 | 164 | switch (cp_entry.tag) { 165 | case CONSTANT_Class: 166 | cp_entry.info.Class.name_index = cf.readShort(bytes, offset); 167 | break; 168 | case CONSTANT_Fieldref: 169 | case CONSTANT_Methodref: 170 | case CONSTANT_InterfaceMethodref: 171 | cp_entry.info.Fieldref.class_index = cf.readShort(bytes, offset); 172 | cp_entry.info.Fieldref.name_and_type_index = 173 | cf.readShort(bytes, offset); 174 | break; 175 | case CONSTANT_String: 176 | cp_entry.info.String.string_index = cf.readShort(bytes, offset); 177 | break; 178 | case CONSTANT_Integer: 179 | cp_entry.info.Integer.bytes = cf.readInt(bytes, offset); 180 | break; 181 | /* 182 | case CONSTANT_Float: 183 | cp_entry.info.Float.bytes = cf.readInt(bytes, offset); 184 | break; 185 | case CONSTANT_Long: 186 | cp_entry.info.Long.bytes = cf.readLong(bytes, offset); 187 | i++; // Long occupies two entries in the constant pool 188 | break; 189 | */ 190 | case CONSTANT_Double: 191 | cp_entry.info.Double.bytes = cf.readDouble(bytes, offset); 192 | i++; // Double occupies two entries in the constant pool 193 | break; 194 | case CONSTANT_NameAndType: 195 | cp_entry.info.NameAndType.name_index = cf.readShort(bytes, offset); 196 | cp_entry.info.NameAndType.descriptor_index = 197 | cf.readShort(bytes, offset); 198 | break; 199 | 200 | case CONSTANT_Utf8: 201 | cp_entry.info.Utf8.length = cf.readShort(bytes, offset); 202 | cp_entry.info.Utf8.bytes = new uint8_t[cp_entry.info.Utf8.length]; 203 | std::memcpy(cp_entry.info.Utf8.bytes, &bytes[offset], 204 | cp_entry.info.Utf8.length); 205 | offset += cp_entry.info.Utf8.length; 206 | break; 207 | /* 208 | case CONSTANT_MethodHandle: 209 | cp_entry.info.MethodHandle.reference_kind = bytes[offset++]; 210 | cp_entry.info.MethodHandle.reference_index = cf.readShort(bytes, offset); 211 | break; 212 | case CONSTANT_MethodType: 213 | cp_entry.info.MethodType.descriptor_index = cf.readShort(bytes, offset); 214 | break; 215 | case CONSTANT_InvokeDynamic: 216 | cp_entry.info.InvokeDynamic.bootstrap_method_attr_index = cf.readShort(bytes, offset); 217 | cp_entry.info.InvokeDynamic.name_and_type_index = cf.readShort(bytes, offset); 218 | break; 219 | */ 220 | default: 221 | spdlog::error("Unsupported Constant Pool Tag: {}! At location i: {})", 222 | cp_entry.tag, i); 223 | offset += 2; 224 | break; 225 | } 226 | } 227 | } 228 | 229 | void parseFields(const uint8_t* bytes, size_t& offset, Classfile& cf) { 230 | cf.fields.resize(cf.fields_count); 231 | for (size_t i = 0; i < cf.fields_count; ++i) { 232 | cf.fields[i].access_flags = cf.readShort(bytes, offset); 233 | cf.fields[i].name_index = cf.readShort(bytes, offset); 234 | cf.fields[i].descriptor_index = cf.readShort(bytes, offset); 235 | cf.fields[i].attributes_count = cf.readShort(bytes, offset); 236 | cf.fields[i].attributes.resize(cf.fields[i].attributes_count); 237 | for (size_t j = 0; j < cf.fields[i].attributes_count; ++j) { 238 | cf.fields[i].attributes[j].attribute_name_index = 239 | cf.readShort(bytes, offset); 240 | cf.fields[i].attributes[j].attribute_length = cf.readInt(bytes, offset); 241 | cf.fields[i].attributes[j].info = 242 | new uint8_t[cf.fields[i].attributes[j].attribute_length]; 243 | std::memcpy(cf.fields[i].attributes[j].info, &bytes[offset], 244 | cf.fields[i].attributes[j].attribute_length); 245 | offset += cf.fields[i].attributes[j].attribute_length; 246 | } 247 | } 248 | } 249 | 250 | void parseMethods(const uint8_t* bytes, size_t& offset, Classfile& cf) { 251 | cf.methods.resize(cf.methods_count); 252 | for (size_t i = 0; i < cf.methods_count; ++i) { 253 | cf.methods[i].access_flags = cf.readShort(bytes, offset); 254 | cf.methods[i].name_index = cf.readShort(bytes, offset); 255 | cf.methods[i].descriptor_index = cf.readShort(bytes, offset); 256 | cf.methods[i].attributes_count = cf.readShort(bytes, offset); 257 | cf.methods[i].attributes.resize(cf.methods[i].attributes_count); 258 | for (size_t j = 0; j < cf.methods[i].attributes_count; ++j) { 259 | cf.methods[i].attributes[j].attribute_name_index = 260 | cf.readShort(bytes, offset); 261 | cf.methods[i].attributes[j].attribute_length = cf.readInt(bytes, offset); 262 | cf.methods[i].attributes[j].info = 263 | new uint8_t[cf.methods[i].attributes[j].attribute_length]; 264 | std::memcpy(cf.methods[i].attributes[j].info, &bytes[offset], 265 | cf.methods[i].attributes[j].attribute_length); 266 | offset += cf.methods[i].attributes[j].attribute_length; 267 | } 268 | } 269 | } 270 | 271 | void parseAttributes(const uint8_t* bytes, size_t& offset, Classfile& cf) { 272 | cf.attributes.resize(cf.attributes_count); 273 | for (size_t i = 0; i < cf.attributes_count; ++i) { 274 | cf.attributes[i].attribute_name_index = cf.readShort(bytes, offset); 275 | cf.attributes[i].attribute_length = cf.readInt(bytes, offset); 276 | cf.attributes[i].info = new uint8_t[cf.attributes[i].attribute_length]; 277 | std::memcpy(cf.attributes[i].info, &bytes[offset], 278 | cf.attributes[i].attribute_length); 279 | offset += cf.attributes[i].attribute_length; 280 | } 281 | } 282 | 283 | /** 284 | * Helper methods ends 285 | */ 286 | 287 | Classfile Classfile::parseClassfile(const uint8_t* classBytes, 288 | size_t fileSize) { 289 | size_t offset = 0; 290 | Classfile cf; 291 | 292 | // Read magic number 293 | cf.magic = readInt(classBytes, offset); 294 | if (!validateMagicNumber(cf.magic)) { 295 | throw std::runtime_error("The magic number of classfile is not valid."); 296 | } else { 297 | spdlog::info("{} Magic number validated", LOG_OK); 298 | } 299 | cf.minor_version = readShort(classBytes, offset); 300 | cf.major_version = readShort(classBytes, offset); 301 | cf.constant_pool_count = readShort(classBytes, offset); 302 | parseConstantPool(classBytes, offset, cf); 303 | 304 | spdlog::info("Parsing Constant Pool has been completed"); 305 | cf.access_flags = readShort(classBytes, offset); 306 | cf.this_class = readShort(classBytes, offset); 307 | cf.super_class = readShort(classBytes, offset); 308 | cf.interfaces_count = readShort(classBytes, offset); 309 | cf.interfaces.resize(cf.interfaces_count); 310 | for (size_t i = 0; i < cf.interfaces_count; ++i) { 311 | cf.interfaces[i] = readShort(classBytes, offset); 312 | } 313 | cf.fields_count = readShort(classBytes, offset); 314 | parseFields(classBytes, offset, cf); 315 | 316 | cf.methods_count = readShort(classBytes, offset); 317 | parseMethods(classBytes, offset, cf); 318 | 319 | cf.attributes_count = readShort(classBytes, offset); 320 | parseAttributes(classBytes, offset, cf); 321 | std::string classFileString = cf.to_string(); 322 | spdlog::info("The Parsed Classfile:\n"); 323 | spdlog::info("{}", classFileString); 324 | 325 | if (offset == fileSize) { 326 | spdlog::info( 327 | "{} Loading classfile has done at offset: {}, for filesize: {}", LOG_OK, 328 | offset, fileSize); 329 | } else { 330 | spdlog::warn( 331 | "{} While loading classfile, the offset:{} does not match with " 332 | "filesize: {}. The parsing might be wrong!", 333 | LOG_NOK, offset, fileSize); 334 | } 335 | return cf; 336 | } 337 | -------------------------------------------------------------------------------- /sample/javap_AddMain.txt: -------------------------------------------------------------------------------- 1 | Classfile /home/levent/Projects/CVM/sample/AddMain.class 2 | Last modified Jun 22, 2024; size 347 bytes 3 | SHA-256 checksum 14811c1816314b13c819db3a96c61676607f69186573fc379f79bd6562982da8 4 | Compiled from "AddMain.java" 5 | class AddMain 6 | minor version: 0 7 | major version: 66 8 | flags: (0x0020) ACC_SUPER 9 | this_class: #8 // AddMain 10 | super_class: #2 // java/lang/Object 11 | interfaces: 0, fields: 0, methods: 3, attributes: 1 12 | Constant pool: 13 | #1 = Methodref #2.#3 // java/lang/Object."":()V 14 | #2 = Class #4 // java/lang/Object 15 | #3 = NameAndType #5:#6 // "":()V 16 | #4 = Utf8 java/lang/Object 17 | #5 = Utf8 18 | #6 = Utf8 ()V 19 | #7 = Methodref #8.#9 // AddMain.add:(II)I 20 | #8 = Class #10 // AddMain 21 | #9 = NameAndType #11:#12 // add:(II)I 22 | #10 = Utf8 AddMain 23 | #11 = Utf8 add 24 | #12 = Utf8 (II)I 25 | #13 = Utf8 Code 26 | #14 = Utf8 LineNumberTable 27 | #15 = Utf8 main 28 | #16 = Utf8 ([Ljava/lang/String;)V 29 | #17 = Utf8 SourceFile 30 | #18 = Utf8 AddMain.java 31 | { 32 | AddMain(); 33 | descriptor: ()V 34 | flags: (0x0000) 35 | Code: 36 | stack=1, locals=1, args_size=1 37 | 0: aload_0 38 | 1: invokespecial #1 // Method java/lang/Object."":()V 39 | 4: return 40 | LineNumberTable: 41 | line 1: 0 42 | 43 | public static void main(java.lang.String[]); 44 | descriptor: ([Ljava/lang/String;)V 45 | flags: (0x0009) ACC_PUBLIC, ACC_STATIC 46 | Code: 47 | stack=2, locals=3, args_size=1 48 | 0: iconst_1 49 | 1: istore_1 50 | 2: iconst_2 51 | 3: istore_2 52 | 4: iload_1 53 | 5: iload_2 54 | 6: invokestatic #7 // Method add:(II)I 55 | 9: pop 56 | 10: return 57 | LineNumberTable: 58 | line 4: 0 59 | line 5: 2 60 | line 6: 4 61 | line 7: 10 62 | 63 | public static int add(int, int); 64 | descriptor: (II)I 65 | flags: (0x0009) ACC_PUBLIC, ACC_STATIC 66 | Code: 67 | stack=2, locals=2, args_size=2 68 | 0: iload_0 69 | 1: iload_1 70 | 2: iadd 71 | 3: ireturn 72 | LineNumberTable: 73 | line 10: 0 74 | } 75 | SourceFile: "AddMain.java" 76 | Classfile /home/levent/Projects/CVM/sample/AddMain.class 77 | Last modified Jun 22, 2024; size 349 bytes 78 | SHA-256 checksum 51968c311d074303b8bebc57dd66676d30055945651608ab6a2a303dd3204ac7 79 | Compiled from "AddMain.java" 80 | class AddMain 81 | minor version: 0 82 | major version: 66 83 | flags: (0x0020) ACC_SUPER 84 | this_class: #8 // AddMain 85 | super_class: #2 // java/lang/Object 86 | interfaces: 0, fields: 0, methods: 3, attributes: 1 87 | Constant pool: 88 | #1 = Methodref #2.#3 // java/lang/Object."":()V 89 | #2 = Class #4 // java/lang/Object 90 | #3 = NameAndType #5:#6 // "":()V 91 | #4 = Utf8 java/lang/Object 92 | #5 = Utf8 93 | #6 = Utf8 ()V 94 | #7 = Methodref #8.#9 // AddMain.add:(II)I 95 | #8 = Class #10 // AddMain 96 | #9 = NameAndType #11:#12 // add:(II)I 97 | #10 = Utf8 AddMain 98 | #11 = Utf8 add 99 | #12 = Utf8 (II)I 100 | #13 = Utf8 Code 101 | #14 = Utf8 LineNumberTable 102 | #15 = Utf8 main 103 | #16 = Utf8 ([Ljava/lang/String;)V 104 | #17 = Utf8 SourceFile 105 | #18 = Utf8 AddMain.java 106 | { 107 | AddMain(); 108 | descriptor: ()V 109 | flags: (0x0000) 110 | Code: 111 | stack=1, locals=1, args_size=1 112 | 0: aload_0 113 | 1: invokespecial #1 // Method java/lang/Object."":()V 114 | 4: return 115 | LineNumberTable: 116 | line 1: 0 117 | 118 | public static void main(java.lang.String[]); 119 | descriptor: ([Ljava/lang/String;)V 120 | flags: (0x0009) ACC_PUBLIC, ACC_STATIC 121 | Code: 122 | stack=2, locals=3, args_size=1 123 | 0: bipush 14 124 | 2: istore_1 125 | 3: bipush 25 126 | 5: istore_2 127 | 6: iload_1 128 | 7: iload_2 129 | 8: invokestatic #7 // Method add:(II)I 130 | 11: pop 131 | 12: return 132 | LineNumberTable: 133 | line 4: 0 134 | line 5: 3 135 | line 6: 6 136 | line 7: 12 137 | 138 | public static int add(int, int); 139 | descriptor: (II)I 140 | flags: (0x0009) ACC_PUBLIC, ACC_STATIC 141 | Code: 142 | stack=2, locals=2, args_size=2 143 | 0: iload_0 144 | 1: iload_1 145 | 2: iadd 146 | 3: ireturn 147 | LineNumberTable: 148 | line 10: 0 149 | } 150 | SourceFile: "AddMain.java" 151 | Classfile /home/levent/Projects/CVM/sample/AddMain.class 152 | Last modified Jun 22, 2024; size 349 bytes 153 | SHA-256 checksum 539564bfbc4e1351399408117b2917887b1fa54f643692643b467d55aaa76bd9 154 | Compiled from "AddMain.java" 155 | class AddMain 156 | minor version: 0 157 | major version: 66 158 | flags: (0x0020) ACC_SUPER 159 | this_class: #8 // AddMain 160 | super_class: #2 // java/lang/Object 161 | interfaces: 0, fields: 0, methods: 3, attributes: 1 162 | Constant pool: 163 | #1 = Methodref #2.#3 // java/lang/Object."":()V 164 | #2 = Class #4 // java/lang/Object 165 | #3 = NameAndType #5:#6 // "":()V 166 | #4 = Utf8 java/lang/Object 167 | #5 = Utf8 168 | #6 = Utf8 ()V 169 | #7 = Methodref #8.#9 // AddMain.add:(II)I 170 | #8 = Class #10 // AddMain 171 | #9 = NameAndType #11:#12 // add:(II)I 172 | #10 = Utf8 AddMain 173 | #11 = Utf8 add 174 | #12 = Utf8 (II)I 175 | #13 = Utf8 Code 176 | #14 = Utf8 LineNumberTable 177 | #15 = Utf8 main 178 | #16 = Utf8 ([Ljava/lang/String;)V 179 | #17 = Utf8 SourceFile 180 | #18 = Utf8 AddMain.java 181 | { 182 | AddMain(); 183 | descriptor: ()V 184 | flags: (0x0000) 185 | Code: 186 | stack=1, locals=1, args_size=1 187 | 0: aload_0 188 | 1: invokespecial #1 // Method java/lang/Object."":()V 189 | 4: return 190 | LineNumberTable: 191 | line 1: 0 192 | 193 | public static void main(java.lang.String[]); 194 | descriptor: ([Ljava/lang/String;)V 195 | flags: (0x0009) ACC_PUBLIC, ACC_STATIC 196 | Code: 197 | stack=2, locals=3, args_size=1 198 | 0: bipush 14 199 | 2: istore_1 200 | 3: bipush 15 201 | 5: istore_2 202 | 6: iload_1 203 | 7: iload_2 204 | 8: invokestatic #7 // Method add:(II)I 205 | 11: pop 206 | 12: return 207 | LineNumberTable: 208 | line 4: 0 209 | line 5: 3 210 | line 6: 6 211 | line 7: 12 212 | 213 | public static int add(int, int); 214 | descriptor: (II)I 215 | flags: (0x0009) ACC_PUBLIC, ACC_STATIC 216 | Code: 217 | stack=2, locals=2, args_size=2 218 | 0: iload_0 219 | 1: iload_1 220 | 2: iadd 221 | 3: ireturn 222 | LineNumberTable: 223 | line 10: 0 224 | } 225 | SourceFile: "AddMain.java" 226 | Classfile /home/levent/Projects/CVM/sample/AddMain.class 227 | Last modified Jun 22, 2024; size 344 bytes 228 | SHA-256 checksum cfc5738c9de9d7cd704372a808960c2c054d836aa52edb6b489e47d0d2ced72f 229 | Compiled from "AddMain.java" 230 | class AddMain 231 | minor version: 0 232 | major version: 66 233 | flags: (0x0020) ACC_SUPER 234 | this_class: #8 // AddMain 235 | super_class: #2 // java/lang/Object 236 | interfaces: 0, fields: 0, methods: 3, attributes: 1 237 | Constant pool: 238 | #1 = Methodref #2.#3 // java/lang/Object."":()V 239 | #2 = Class #4 // java/lang/Object 240 | #3 = NameAndType #5:#6 // "":()V 241 | #4 = Utf8 java/lang/Object 242 | #5 = Utf8 243 | #6 = Utf8 ()V 244 | #7 = Methodref #8.#9 // AddMain.add:(II)I 245 | #8 = Class #10 // AddMain 246 | #9 = NameAndType #11:#12 // add:(II)I 247 | #10 = Utf8 AddMain 248 | #11 = Utf8 add 249 | #12 = Utf8 (II)I 250 | #13 = Utf8 Code 251 | #14 = Utf8 LineNumberTable 252 | #15 = Utf8 main 253 | #16 = Utf8 ([Ljava/lang/String;)I 254 | #17 = Utf8 SourceFile 255 | #18 = Utf8 AddMain.java 256 | { 257 | AddMain(); 258 | descriptor: ()V 259 | flags: (0x0000) 260 | Code: 261 | stack=1, locals=1, args_size=1 262 | 0: aload_0 263 | 1: invokespecial #1 // Method java/lang/Object."":()V 264 | 4: return 265 | LineNumberTable: 266 | line 1: 0 267 | 268 | public static int main(java.lang.String[]); 269 | descriptor: ([Ljava/lang/String;)I 270 | flags: (0x0009) ACC_PUBLIC, ACC_STATIC 271 | Code: 272 | stack=2, locals=3, args_size=1 273 | 0: bipush 14 274 | 2: istore_1 275 | 3: bipush 15 276 | 5: istore_2 277 | 6: iload_1 278 | 7: iload_2 279 | 8: invokestatic #7 // Method add:(II)I 280 | 11: ireturn 281 | LineNumberTable: 282 | line 4: 0 283 | line 5: 3 284 | line 6: 6 285 | 286 | public static int add(int, int); 287 | descriptor: (II)I 288 | flags: (0x0009) ACC_PUBLIC, ACC_STATIC 289 | Code: 290 | stack=2, locals=2, args_size=2 291 | 0: iload_0 292 | 1: iload_1 293 | 2: iadd 294 | 3: ireturn 295 | LineNumberTable: 296 | line 10: 0 297 | } 298 | SourceFile: "AddMain.java" 299 | Classfile /home/levent/Projects/CVM/sample/AddMain.class 300 | Last modified Jun 22, 2024; size 282 bytes 301 | SHA-256 checksum 686603e16d220775966c2568e490983b83c5b5e5725340e919941eec59436e23 302 | Compiled from "AddMain.java" 303 | class AddMain 304 | minor version: 0 305 | major version: 66 306 | flags: (0x0020) ACC_SUPER 307 | this_class: #7 // AddMain 308 | super_class: #2 // java/lang/Object 309 | interfaces: 0, fields: 0, methods: 2, attributes: 1 310 | Constant pool: 311 | #1 = Methodref #2.#3 // java/lang/Object."":()V 312 | #2 = Class #4 // java/lang/Object 313 | #3 = NameAndType #5:#6 // "":()V 314 | #4 = Utf8 java/lang/Object 315 | #5 = Utf8 316 | #6 = Utf8 ()V 317 | #7 = Class #8 // AddMain 318 | #8 = Utf8 AddMain 319 | #9 = Utf8 Code 320 | #10 = Utf8 LineNumberTable 321 | #11 = Utf8 main 322 | #12 = Utf8 ([Ljava/lang/String;)I 323 | #13 = Utf8 SourceFile 324 | #14 = Utf8 AddMain.java 325 | { 326 | AddMain(); 327 | descriptor: ()V 328 | flags: (0x0000) 329 | Code: 330 | stack=1, locals=1, args_size=1 331 | 0: aload_0 332 | 1: invokespecial #1 // Method java/lang/Object."":()V 333 | 4: return 334 | LineNumberTable: 335 | line 1: 0 336 | 337 | public static int main(java.lang.String[]); 338 | descriptor: ([Ljava/lang/String;)I 339 | flags: (0x0009) ACC_PUBLIC, ACC_STATIC 340 | Code: 341 | stack=2, locals=4, args_size=1 342 | 0: bipush 14 343 | 2: istore_1 344 | 3: bipush 15 345 | 5: istore_2 346 | 6: iload_1 347 | 7: iload_2 348 | 8: iadd 349 | 9: istore_3 350 | 10: iload_3 351 | 11: ireturn 352 | LineNumberTable: 353 | line 4: 0 354 | line 5: 3 355 | line 6: 6 356 | line 7: 10 357 | } 358 | SourceFile: "AddMain.java" 359 | Classfile /home/levent/Projects/CVM/sample/AddMain.class 360 | Last modified Jun 22, 2024; size 282 bytes 361 | SHA-256 checksum 686603e16d220775966c2568e490983b83c5b5e5725340e919941eec59436e23 362 | Compiled from "AddMain.java" 363 | class AddMain 364 | minor version: 0 365 | major version: 66 366 | flags: (0x0020) ACC_SUPER 367 | this_class: #7 // AddMain 368 | super_class: #2 // java/lang/Object 369 | interfaces: 0, fields: 0, methods: 2, attributes: 1 370 | Constant pool: 371 | #1 = Methodref #2.#3 // java/lang/Object."":()V 372 | #2 = Class #4 // java/lang/Object 373 | #3 = NameAndType #5:#6 // "":()V 374 | #4 = Utf8 java/lang/Object 375 | #5 = Utf8 376 | #6 = Utf8 ()V 377 | #7 = Class #8 // AddMain 378 | #8 = Utf8 AddMain 379 | #9 = Utf8 Code 380 | #10 = Utf8 LineNumberTable 381 | #11 = Utf8 main 382 | #12 = Utf8 ([Ljava/lang/String;)I 383 | #13 = Utf8 SourceFile 384 | #14 = Utf8 AddMain.java 385 | { 386 | AddMain(); 387 | descriptor: ()V 388 | flags: (0x0000) 389 | Code: 390 | stack=1, locals=1, args_size=1 391 | 0: aload_0 392 | 1: invokespecial #1 // Method java/lang/Object."":()V 393 | 4: return 394 | LineNumberTable: 395 | line 1: 0 396 | 397 | public static int main(java.lang.String[]); 398 | descriptor: ([Ljava/lang/String;)I 399 | flags: (0x0009) ACC_PUBLIC, ACC_STATIC 400 | Code: 401 | stack=2, locals=4, args_size=1 402 | 0: bipush 14 403 | 2: istore_1 404 | 3: bipush 15 405 | 5: istore_2 406 | 6: iload_1 407 | 7: iload_2 408 | 8: iadd 409 | 9: istore_3 410 | 10: iload_3 411 | 11: ireturn 412 | LineNumberTable: 413 | line 4: 0 414 | line 5: 3 415 | line 6: 6 416 | line 7: 10 417 | } 418 | SourceFile: "AddMain.java" 419 | --------------------------------------------------------------------------------