├── tests ├── data │ ├── Anno.class │ ├── ArrayTest.class │ ├── ArrowDump.class │ ├── JumpInit.class │ ├── OakTest.class │ ├── RecArray.class │ ├── TcbTest.class │ ├── BranchTest.class │ ├── FinallyTest.class │ ├── ForLoopTest.class │ ├── GraxCrackMe.class │ ├── HelloWorld.class │ ├── InnerClass.class │ ├── SwitchTest.class │ ├── TernaryTest.class │ ├── AnnotationTest.class │ ├── FinallyTest2.class │ ├── InterfaceTest.class │ ├── InvalidOpcode.class │ ├── LongInitTest.class │ ├── CalculationTest.class │ ├── ForLoopTestLong.class │ ├── InnerClass$Inner.class │ ├── LocalAnnotation.class │ ├── LookupSwitchTest.class │ ├── TableSwitchTest.class │ ├── InvokeDynamicTest.class │ ├── NestedForLoopTest.class │ ├── ParameterFrameTest.class │ ├── LoadConstantClassTest.class │ ├── SwitchTest$InnerAnnotation.class │ ├── InterfaceTest$InnerAnnotation.class │ ├── LocalAnnotation$TestElement.class │ ├── LocalAnnotation$CoolAnnotation.class │ ├── HelloWorld.java │ ├── LoadConstantClassTest.java │ ├── InnerClass.java │ ├── ForLoopTest.java │ ├── ForLoopTestLong.java │ ├── TernaryTest.java │ ├── InvokeDynamicTest.java │ ├── NestedForLoopTest.java │ ├── FinallyTest2.java │ ├── FinallyTest.java │ ├── BranchTest.java │ ├── JumpInit.java │ ├── RecArray.java │ ├── LookupSwitchTest.java │ ├── TableSwitchTest.java │ ├── LongInitTest.java │ ├── InterfaceTest.java │ ├── LocalAnnotation.java │ ├── ParameterFrameTest.java │ ├── SwitchTest.java │ ├── ArrayTest.java │ ├── TcbTest.java │ ├── CalculationTest.java │ └── AnnotationTest.java ├── class_tree_test.cpp ├── class_reader_test.cpp ├── CMakeLists.txt ├── desc_parse_test.cpp ├── class_writer_test.cpp └── analysis_test.cpp ├── gen ├── CMakeLists.txt └── gen.cpp ├── .gitignore ├── include ├── cafe │ ├── apidef.hpp │ ├── label.hpp │ ├── data_reader.hpp │ ├── class_tree.hpp │ ├── class_reader.hpp │ ├── data_writer.hpp │ ├── constant_pool.hpp │ ├── value.hpp │ ├── analysis.hpp │ ├── class_writer.hpp │ ├── result.hpp │ ├── annotation.hpp │ ├── constants.hpp │ ├── class_file.hpp │ └── instruction.hpp └── hippo │ └── cafe.hpp ├── src ├── result.cpp ├── label.cpp ├── visitor.hpp ├── visitor.cpp ├── class_tree.cpp ├── annotation.cpp ├── data_reader.cpp ├── value.cpp ├── data_writer.cpp ├── class_file.cpp ├── constants.cpp └── instruction.cpp ├── Config.cmake.in ├── LICENSE ├── .clang-format ├── README.md └── CMakeLists.txt /tests/data/Anno.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/Anno.class -------------------------------------------------------------------------------- /tests/data/ArrayTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/ArrayTest.class -------------------------------------------------------------------------------- /tests/data/ArrowDump.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/ArrowDump.class -------------------------------------------------------------------------------- /tests/data/JumpInit.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/JumpInit.class -------------------------------------------------------------------------------- /tests/data/OakTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/OakTest.class -------------------------------------------------------------------------------- /tests/data/RecArray.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/RecArray.class -------------------------------------------------------------------------------- /tests/data/TcbTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/TcbTest.class -------------------------------------------------------------------------------- /tests/data/BranchTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/BranchTest.class -------------------------------------------------------------------------------- /tests/data/FinallyTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/FinallyTest.class -------------------------------------------------------------------------------- /tests/data/ForLoopTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/ForLoopTest.class -------------------------------------------------------------------------------- /tests/data/GraxCrackMe.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/GraxCrackMe.class -------------------------------------------------------------------------------- /tests/data/HelloWorld.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/HelloWorld.class -------------------------------------------------------------------------------- /tests/data/InnerClass.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/InnerClass.class -------------------------------------------------------------------------------- /tests/data/SwitchTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/SwitchTest.class -------------------------------------------------------------------------------- /tests/data/TernaryTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/TernaryTest.class -------------------------------------------------------------------------------- /tests/data/AnnotationTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/AnnotationTest.class -------------------------------------------------------------------------------- /tests/data/FinallyTest2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/FinallyTest2.class -------------------------------------------------------------------------------- /tests/data/InterfaceTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/InterfaceTest.class -------------------------------------------------------------------------------- /tests/data/InvalidOpcode.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/InvalidOpcode.class -------------------------------------------------------------------------------- /tests/data/LongInitTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/LongInitTest.class -------------------------------------------------------------------------------- /tests/data/CalculationTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/CalculationTest.class -------------------------------------------------------------------------------- /tests/data/ForLoopTestLong.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/ForLoopTestLong.class -------------------------------------------------------------------------------- /tests/data/InnerClass$Inner.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/InnerClass$Inner.class -------------------------------------------------------------------------------- /tests/data/LocalAnnotation.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/LocalAnnotation.class -------------------------------------------------------------------------------- /tests/data/LookupSwitchTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/LookupSwitchTest.class -------------------------------------------------------------------------------- /tests/data/TableSwitchTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/TableSwitchTest.class -------------------------------------------------------------------------------- /tests/data/InvokeDynamicTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/InvokeDynamicTest.class -------------------------------------------------------------------------------- /tests/data/NestedForLoopTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/NestedForLoopTest.class -------------------------------------------------------------------------------- /tests/data/ParameterFrameTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/ParameterFrameTest.class -------------------------------------------------------------------------------- /tests/data/LoadConstantClassTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/LoadConstantClassTest.class -------------------------------------------------------------------------------- /tests/data/SwitchTest$InnerAnnotation.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/SwitchTest$InnerAnnotation.class -------------------------------------------------------------------------------- /tests/data/InterfaceTest$InnerAnnotation.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/InterfaceTest$InnerAnnotation.class -------------------------------------------------------------------------------- /tests/data/LocalAnnotation$TestElement.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/LocalAnnotation$TestElement.class -------------------------------------------------------------------------------- /tests/data/LocalAnnotation$CoolAnnotation.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hippo/HippoCafe/HEAD/tests/data/LocalAnnotation$CoolAnnotation.class -------------------------------------------------------------------------------- /gen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(libzip REQUIRED) 2 | 3 | add_executable(hippocafe_gen gen.cpp) 4 | 5 | target_link_libraries(hippocafe_gen PRIVATE hippocafe libzip::zip) -------------------------------------------------------------------------------- /tests/data/HelloWorld.java: -------------------------------------------------------------------------------- 1 | public final class HelloWorld { 2 | 3 | public static void main(String[] args) { 4 | System.out.println("Hello World"); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/data/LoadConstantClassTest.java: -------------------------------------------------------------------------------- 1 | public final class LoadConstantClassTest { 2 | 3 | public static void main(String[] args) { 4 | System.out.println(System.class); 5 | } 6 | } -------------------------------------------------------------------------------- /tests/data/InnerClass.java: -------------------------------------------------------------------------------- 1 | public final class InnerClass { 2 | public static class Inner { 3 | public void test() { 4 | System.out.println("Test"); 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # CMake 2 | cmake-build-debug/ 3 | cmake-build-release/ 4 | /out 5 | /build/ 6 | 7 | # IDE 8 | .idea/ 9 | .vs/ 10 | .cache/ 11 | 12 | # macOS 13 | .DS_Store 14 | 15 | # Compiled / Testing 16 | *.jar -------------------------------------------------------------------------------- /tests/data/ForLoopTest.java: -------------------------------------------------------------------------------- 1 | public final class ForLoopTest { 2 | public static void main(String[] args) { 3 | for (int i = 0; i < 5; i++) { 4 | System.out.println("Loop -> " + i); 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/data/ForLoopTestLong.java: -------------------------------------------------------------------------------- 1 | public final class ForLoopTestLong { 2 | public static void main(String[] args) { 3 | for (long i = 0; i < 5; i++) { 4 | System.out.println("Loop -> " + i); 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /include/cafe/apidef.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef CAFE_SHARED_LIB 4 | #ifdef CAFE_EXPORTS 5 | #define CAFE_API __declspec(dllexport) 6 | #else 7 | #define CAFE_API __declspec(dllimport) 8 | #endif 9 | #else 10 | #define CAFE_API 11 | #endif -------------------------------------------------------------------------------- /tests/data/TernaryTest.java: -------------------------------------------------------------------------------- 1 | import java.util.Random; 2 | 3 | public final class TernaryTest { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(new Random().nextBoolean() ? "True" : "False"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/class_tree_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(class_tree_test, assignable_from) { 5 | cafe::class_tree tree(cafe::load_rt); 6 | 7 | std::cout << tree.is_assignable_from("java/lang/Object", "[I") << std::endl; 8 | } -------------------------------------------------------------------------------- /src/result.cpp: -------------------------------------------------------------------------------- 1 | #include "cafe/result.hpp" 2 | 3 | cafe::error::error(const std::string& message) : message_(message) { 4 | } 5 | cafe::error::error(std::string&& message) : message_(std::move(message)) { 6 | } 7 | const std::string& cafe::error::message() const { 8 | return message_; 9 | } 10 | -------------------------------------------------------------------------------- /tests/data/InvokeDynamicTest.java: -------------------------------------------------------------------------------- 1 | public final class InvokeDynamicTest { 2 | 3 | public static void main(String[] args) { 4 | invoke(() -> System.out.println("Hello from dynamic")); 5 | } 6 | 7 | private static void invoke(Runnable runnable) { 8 | runnable.run(); 9 | } 10 | } -------------------------------------------------------------------------------- /tests/data/NestedForLoopTest.java: -------------------------------------------------------------------------------- 1 | public final class NestedForLoopTest { 2 | public static void main(String[] args) { 3 | for (int i = 0; i < 5; i++) { 4 | for (int j = 0; j < 5; j++) { 5 | System.out.println("Loop " + (i * j)); 6 | } 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /tests/data/FinallyTest2.java: -------------------------------------------------------------------------------- 1 | public final class FinallyTest2 { 2 | public static void main(String[] args) { 3 | try { 4 | System.out.println("In tcb"); 5 | } catch (Exception e) { 6 | e.printStackTrace(); 7 | } finally { 8 | System.out.println("Finally block"); 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tests/data/FinallyTest.java: -------------------------------------------------------------------------------- 1 | public final class FinallyTest { 2 | public static void main(String[] args) { 3 | try { 4 | throw new RuntimeException("Wow this is cool"); 5 | } catch (Exception e) { 6 | e.printStackTrace(); 7 | } finally { 8 | System.out.println("Finally block"); 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tests/data/BranchTest.java: -------------------------------------------------------------------------------- 1 | import java.util.Random; 2 | 3 | public final class BranchTest { 4 | 5 | public static void main(String[] args) { 6 | if (new Random().nextBoolean()) { 7 | System.out.println("True"); 8 | } else { 9 | System.out.println("False"); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/data/JumpInit.java: -------------------------------------------------------------------------------- 1 | import java.util.Random; 2 | 3 | public final class JumpInit { 4 | private JumpInit() { 5 | if (new Random().nextBoolean()) { 6 | System.out.println("True"); 7 | } else { 8 | System.out.println("False"); 9 | } 10 | } 11 | 12 | public static void main(String[] args) { 13 | new JumpInit(); 14 | } 15 | } -------------------------------------------------------------------------------- /tests/data/RecArray.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | 3 | public class RecArray { 4 | 5 | public static void main(String[] args) { 6 | for (int i = 0; i < 5; i++) { 7 | ArrayList list = new ArrayList<>(); 8 | for (int j = 0; j < 5; j++) { 9 | list.add(j * i); 10 | } 11 | System.out.println(list); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /tests/data/LookupSwitchTest.java: -------------------------------------------------------------------------------- 1 | public final class LookupSwitchTest { 2 | public static void main(String[] args) { 3 | for (int i = 0; i < 2; i++) { 4 | switch (i) { 5 | case 0: 6 | System.out.println("0 case"); 7 | break; 8 | case 1: 9 | System.out.println("1 case"); 10 | } 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Config.cmake.in: -------------------------------------------------------------------------------- 1 | include(CMakePackageConfigHelpers) 2 | 3 | configure_package_config_file( 4 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in" 5 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 6 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 7 | ) 8 | 9 | install(FILES 10 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 11 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 12 | ) -------------------------------------------------------------------------------- /include/hippo/cafe.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cafe/analysis.hpp" 4 | #include "cafe/annotation.hpp" 5 | #include "cafe/class_file.hpp" 6 | #include "cafe/class_reader.hpp" 7 | #include "cafe/class_tree.hpp" 8 | #include "cafe/class_writer.hpp" 9 | #include "cafe/constants.hpp" 10 | #include "cafe/instruction.hpp" 11 | #include "cafe/label.hpp" 12 | #include "cafe/value.hpp" 13 | 14 | namespace cafe { 15 | template 16 | std::string to_string(const T& value) { 17 | return value.to_string(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/data/TableSwitchTest.java: -------------------------------------------------------------------------------- 1 | public class TableSwitchTest { 2 | public static void main(String[] args) { 3 | double test = 0; 4 | for (int i = 0; i < 2; i++) { 5 | test += i * 0.5; 6 | } 7 | test += 1; 8 | switch ((int) test) { 9 | case 1: 10 | System.out.println("1 case"); 11 | break; 12 | case 3: 13 | System.out.println("3 case"); 14 | break; 15 | case 5: 16 | System.out.println("5 case"); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /tests/data/LongInitTest.java: -------------------------------------------------------------------------------- 1 | import java.util.*; 2 | 3 | public class LongInitTest { 4 | 5 | private String test; 6 | private Map map; 7 | private long time; 8 | 9 | public LongInitTest(String test) { 10 | this.test = test; 11 | this.map = new HashMap<>(); 12 | this.time = System.currentTimeMillis(); 13 | } 14 | 15 | public static void main(String[] args) { 16 | LongInitTest test = new LongInitTest("test"); 17 | System.out.println(test.test); 18 | System.out.println(test.map); 19 | System.out.println(test.time); 20 | } 21 | } -------------------------------------------------------------------------------- /tests/data/InterfaceTest.java: -------------------------------------------------------------------------------- 1 | 2 | public final class InterfaceTest implements java.util.function.Predicate { 3 | 4 | @Deprecated 5 | @InnerAnnotation(test = 69) 6 | private static String testField = "field"; 7 | 8 | public boolean test(String test) { 9 | return true; 10 | } 11 | 12 | public static void main(String[] args) { 13 | int local = 6; 14 | try { 15 | System.out.println(new InterfaceTest().test(testField)); 16 | }catch (Exception e) { 17 | local = 5; 18 | } 19 | System.out.println(local); 20 | } 21 | 22 | public static @interface InnerAnnotation { 23 | int test(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/data/LocalAnnotation.java: -------------------------------------------------------------------------------- 1 | 2 | import java.lang.annotation.ElementType; 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | import java.util.ArrayList; 7 | 8 | public final class LocalAnnotation { 9 | 10 | public static enum TestElement { 11 | FIRST, SECOND 12 | } 13 | 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.TYPE_USE) 16 | public static @interface CoolAnnotation { 17 | 18 | } 19 | 20 | public static void main(String[] args) { 21 | ArrayList integers = new @CoolAnnotation ArrayList<>(); 22 | integers.add(1); 23 | System.out.println(integers); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/data/ParameterFrameTest.java: -------------------------------------------------------------------------------- 1 | import java.util.Arrays; 2 | 3 | public final class ParameterFrameTest { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(testMe("random")); 7 | String[] strings = {"dummylol", "random"}; 8 | System.out.println(Arrays.toString(testMeButAnArray(strings))); 9 | } 10 | 11 | public static String testMe(String a) { 12 | if (a.startsWith("dummy")) { 13 | return null; 14 | } 15 | return a.concat("congrats"); 16 | } 17 | 18 | public static String[] testMeButAnArray(String[] a) { 19 | for (int i = 0; i < a.length; i++) { 20 | String s = a[i]; 21 | if (s.startsWith("dummy")) { 22 | a[i] = null; 23 | } else { 24 | a[i] = s.concat("congrats"); 25 | } 26 | } 27 | return a; 28 | } 29 | } -------------------------------------------------------------------------------- /tests/class_reader_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | TEST(class_reader, test) { 10 | std::string test_name = "TcbTest"; 11 | std::ifstream stream(std::string("data/") + test_name + ".class", std::ios::binary); 12 | 13 | cafe::class_reader reader; 14 | const auto file_res = reader.read(stream); 15 | if (!file_res) { 16 | std::cerr << file_res.err().message() << std::endl; 17 | return; 18 | } 19 | const auto& file = file_res.value(); 20 | 21 | std::cout << file.name << std::endl; 22 | for (const auto& m : file.methods) { 23 | std::cout << m.name << " " << m.desc << std::endl; 24 | for (const auto& in : m.body) { 25 | std::cout << " " << cafe::to_string(in) << std::endl; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | FetchContent_Declare( 3 | googletest 4 | GIT_REPOSITORY https://github.com/google/googletest.git 5 | GIT_TAG v1.14.0 6 | ) 7 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 8 | FetchContent_MakeAvailable(googletest) 9 | 10 | add_executable(hippocafe_test 11 | class_reader_test.cpp 12 | class_writer_test.cpp 13 | desc_parse_test.cpp 14 | class_tree_test.cpp 15 | analysis_test.cpp 16 | ) 17 | 18 | enable_testing() 19 | add_test(NAME hippocafe_test COMMAND hippocafe_test) 20 | target_link_libraries(hippocafe_test PRIVATE gtest_main hippocafe) 21 | 22 | 23 | add_custom_command(TARGET hippocafe_test POST_BUILD 24 | COMMAND ${CMAKE_COMMAND} -E copy_directory 25 | ${CMAKE_CURRENT_SOURCE_DIR}/data 26 | ${CMAKE_CURRENT_BINARY_DIR}/data) -------------------------------------------------------------------------------- /include/cafe/label.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "apidef.hpp" 8 | 9 | namespace cafe { 10 | 11 | using label_id = uint64_t; 12 | 13 | class CAFE_API label { 14 | public: 15 | std::string debug_name; 16 | label(); 17 | explicit label(const std::string_view& debug_name); 18 | label(label_id id, const std::string_view& debug_name); 19 | explicit label(label_id id); 20 | ~label() = default; 21 | label(const label& other); 22 | label(label&&) = default; 23 | label& operator=(const label&) = default; 24 | label& operator=(label&&) = default; 25 | bool operator==(const label& other) const; 26 | bool operator!=(const label& other) const; 27 | label_id id() const; 28 | std::string to_string() const; 29 | 30 | private: 31 | label_id id_; 32 | 33 | static label_id next_id(); 34 | }; 35 | } // namespace cafe 36 | -------------------------------------------------------------------------------- /tests/data/SwitchTest.java: -------------------------------------------------------------------------------- 1 | 2 | public final class SwitchTest implements java.util.function.Predicate { 3 | 4 | @Deprecated 5 | @InnerAnnotation(test = 69) 6 | private static String testField = "field"; 7 | 8 | public boolean test(String test) { 9 | return true; 10 | } 11 | 12 | public static void main(String[] args) { 13 | int local = 6; 14 | try { 15 | System.out.println(new SwitchTest().test(testField)); 16 | }catch (Exception e) { 17 | local = 5; 18 | } 19 | 20 | switch (local) { 21 | case 6: 22 | System.out.println("ye"); 23 | break; 24 | case 5: 25 | System.out.println("ya"); 26 | break; 27 | default: 28 | System.out.println("default"); 29 | } 30 | 31 | System.out.println(local); 32 | } 33 | 34 | public static @interface InnerAnnotation { 35 | int test(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/data/ArrayTest.java: -------------------------------------------------------------------------------- 1 | public class ArrayTest { 2 | 3 | private static long[][] globalWideMultiArray = new long[1][]; 4 | private static String[][] globalObjArray = new String[1][]; 5 | 6 | public static void main(String[] args) { 7 | String[] local = new String[1]; 8 | int[][] localMultiPrim = new int[1][]; 9 | String[][] idekAnyMore = new String[1][1]; 10 | 11 | globalWideMultiArray[0] = new long[] {12}; 12 | globalObjArray[0] = new String[] {"Wow Coool Array"}; 13 | local[0] = "2*2e,1e2*7.6"; 14 | localMultiPrim[0] = new int[] {69}; 15 | idekAnyMore[0][0] = "Ayo Multi anew array"; 16 | 17 | for (long i = 0; i < globalWideMultiArray[0][0]; i++) { 18 | char[] chars = local[0].toCharArray(); 19 | chars[(int) i] ^= localMultiPrim[0][0]; 20 | local[0] = String.valueOf(chars); 21 | } 22 | System.out.println(local[0] + " " + globalObjArray[0][0]); 23 | System.out.println(idekAnyMore[0][0]); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/label.cpp: -------------------------------------------------------------------------------- 1 | #include "cafe/label.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace cafe { 8 | 9 | label::label() : id_(next_id()) { 10 | } 11 | label::label(const std::string_view& debug_name) : debug_name(debug_name), id_(next_id()) { 12 | } 13 | label::label(label_id id, const std::string_view& debug_name) : debug_name(debug_name), id_(id) { 14 | } 15 | label::label(label_id id) : id_(id) { 16 | } 17 | bool label::operator==(const label& other) const { 18 | return id_ == other.id_; 19 | } 20 | bool label::operator!=(const label& other) const { 21 | return id_ != other.id_; 22 | } 23 | label_id label::id() const { 24 | return id_; 25 | } 26 | label::label(const label& other) { 27 | id_ = other.id_; 28 | debug_name = other.debug_name; 29 | } 30 | std::string label::to_string() const { 31 | std::ostringstream oss; 32 | oss << "label_" << debug_name << ":"; 33 | return oss.str(); 34 | } 35 | 36 | label_id label::next_id() { 37 | static std::atomic_uint64_t id{0}; 38 | return id++; 39 | } 40 | } // namespace cafe 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Hippo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /tests/desc_parse_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | static void test_desc(const std::string_view& desc) { 6 | cafe::type t(desc); 7 | std::cout << desc << " -> " << t.get() << std::endl; 8 | const auto params = t.parameter_types(); 9 | const auto ret = t.return_type(); 10 | std::cout << "Params (" << params.size() << "):" << std::endl; 11 | for (const auto& p : params) { 12 | std::cout << " " << p.get() << std::endl; 13 | } 14 | std::cout << "Return: " << ret.get() << std::endl; 15 | } 16 | 17 | TEST(desc, parse) { 18 | std::string desc1 = "(Ljava/lang/String;IJJD[[Ljava/lang/String;)V"; 19 | std::string desc2 = "(Ljava/lang/String;IJ[[JD[[Ljava/lang/String;)Ljava/lang/String;"; 20 | std::string desc3 = "()V"; 21 | 22 | std::cout << "desc1: " << desc1 << std::endl; 23 | test_desc(desc1); 24 | std::cout << "desc2: " << desc2 << std::endl; 25 | test_desc(desc2); 26 | std::cout << "desc3: " << desc3 << std::endl; 27 | test_desc(desc3); 28 | 29 | cafe::class_tree tree; 30 | tree.put("my/ThisClass", "my/SuperClass", {"my/Interface1", "my/Interface2"}); 31 | } 32 | -------------------------------------------------------------------------------- /tests/data/TcbTest.java: -------------------------------------------------------------------------------- 1 | public final class TcbTest { 2 | public static void main(String[] args) { 3 | for (int i = 0; i < 4; i++) { 4 | try { 5 | switch (i) { 6 | case 0: throw new NullPointerException(); 7 | case 1: throw new IllegalStateException(); 8 | case 2: throw new IndexOutOfBoundsException(); 9 | case 3: throw new IllegalArgumentException(); 10 | } 11 | } catch (NullPointerException nullPointerException) { 12 | System.out.println("Null Pointer"); 13 | } catch (IllegalStateException illegalStateException) { 14 | System.out.println("Illegal State"); 15 | } catch (IndexOutOfBoundsException | IllegalArgumentException e) { 16 | System.out.println("Compound"); 17 | try { 18 | if (e instanceof IllegalArgumentException) { 19 | System.out.println("start inner"); 20 | ((Object)null).toString(); 21 | System.out.println("Shouldn't happen"); 22 | } 23 | } catch (NullPointerException e1) { 24 | System.out.println("exception in handler"); 25 | } 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | AlignConsecutiveAssignments: false 5 | AlignConsecutiveDeclarations: false 6 | AlignOperands: true 7 | AlignTrailingComments: false 8 | AllowShortBlocksOnASingleLine: Empty 9 | AllowShortFunctionsOnASingleLine: None 10 | AlwaysBreakTemplateDeclarations: Yes 11 | BraceWrapping: 12 | AfterCaseLabel: false 13 | AfterClass: false 14 | AfterControlStatement: false 15 | AfterEnum: false 16 | AfterFunction: false 17 | AfterNamespace: false 18 | AfterStruct: false 19 | AfterUnion: false 20 | AfterExternBlock: false 21 | BeforeCatch: false 22 | BeforeElse: false 23 | BeforeLambdaBody: false 24 | BeforeWhile: false 25 | SplitEmptyFunction: false 26 | SplitEmptyRecord: false 27 | SplitEmptyNamespace: false 28 | BreakBeforeBraces: Custom 29 | BreakConstructorInitializers: AfterColon 30 | PointerAlignment: Left 31 | ColumnLimit: 120 32 | IncludeCategories: 33 | - Regex: '^<.*' 34 | Priority: 1 35 | - Regex: '^".*' 36 | Priority: 2 37 | - Regex: '.*' 38 | Priority: 3 39 | IncludeIsMainRegex: '([-_](test|unittest))?$' 40 | IndentCaseLabels: true 41 | InsertNewlineAtEOF: true 42 | MacroBlockBegin: '' 43 | MacroBlockEnd: '' 44 | MaxEmptyLinesToKeep: 2 45 | SpaceAfterTemplateKeyword: false 46 | SpaceBeforeRangeBasedForLoopColon: true 47 | SpacesInAngles: false 48 | TabWidth: 2 49 | ... 50 | -------------------------------------------------------------------------------- /include/cafe/data_reader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "result.hpp" 9 | 10 | namespace cafe { 11 | 12 | class data_reader { 13 | public: 14 | data_reader(std::vector&& data); 15 | data_reader(const std::vector& data); 16 | data_reader(std::istream& stream); 17 | ~data_reader() = default; 18 | data_reader(const data_reader&) = delete; 19 | data_reader(data_reader&&) = default; 20 | data_reader& operator=(const data_reader&) = delete; 21 | data_reader& operator=(data_reader&&) = default; 22 | 23 | result read_i8(); 24 | result read_u8(); 25 | result read_i16(); 26 | result read_u16(); 27 | result read_i32(); 28 | result read_u32(); 29 | result read_i64(); 30 | result read_u64(); 31 | result read_f32(); 32 | result read_f64(); 33 | result read_utf(); 34 | 35 | result> bytes(size_t start, size_t end); 36 | 37 | const std::vector& data() const; 38 | std::vector& data(); 39 | 40 | size_t cursor() const; 41 | [[nodiscard]] bool cursor(size_t cursor); 42 | [[nodiscard]] bool skip(size_t count); 43 | 44 | private: 45 | std::vector buffer_; 46 | std::vector byte_cache_; 47 | size_t cursor_ = 0; 48 | }; 49 | 50 | } -------------------------------------------------------------------------------- /src/visitor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../include/cafe/data_writer.hpp" 4 | #include "cafe/class_reader.hpp" 5 | #include "cafe/constant_pool.hpp" 6 | 7 | namespace cafe { 8 | class constant_pool_visitor { 9 | public: 10 | explicit constant_pool_visitor(data_writer& writer); 11 | void operator()(const cp::pad_info&) const; 12 | 13 | void operator()(const cp::class_info& info); 14 | 15 | void operator()(const cp::field_ref_info& info); 16 | 17 | void operator()(const cp::method_ref_info& info); 18 | 19 | void operator()(const cp::interface_method_ref_info& info); 20 | 21 | void operator()(const cp::string_info& info); 22 | 23 | void operator()(const cp::integer_info& info); 24 | 25 | void operator()(const cp::float_info& info); 26 | 27 | void operator()(const cp::long_info& info); 28 | 29 | void operator()(const cp::double_info& info); 30 | 31 | void operator()(const cp::name_and_type_info& info); 32 | 33 | void operator()(const cp::utf8_info& info); 34 | 35 | void operator()(const cp::method_handle_info& info); 36 | 37 | void operator()(const cp::method_type_info& info); 38 | 39 | void operator()(const cp::dynamic_info& info); 40 | 41 | void operator()(const cp::invoke_dynamic_info& info); 42 | 43 | void operator()(const cp::module_info& info); 44 | 45 | void operator()(const cp::package_info& info); 46 | 47 | private: 48 | data_writer& writer_; 49 | }; 50 | 51 | } // namespace cafe 52 | -------------------------------------------------------------------------------- /tests/data/CalculationTest.java: -------------------------------------------------------------------------------- 1 | import java.util.*; 2 | import java.util.function.Function; 3 | import java.util.stream.Collectors; 4 | 5 | public class CalculationTest { 6 | 7 | public static void main(String[] args) { 8 | try { 9 | Map> dataMap = new HashMap<>(); 10 | dataMap.put("First", Arrays.asList(1, 2, 3)); 11 | dataMap.put("Second", Arrays.asList(4, 5, 6)); 12 | 13 | int calculationResult = complexDataProcessing(dataMap); 14 | System.out.println("Calculation Result: " + calculationResult); 15 | } catch (Exception e) { 16 | System.err.println("Error: " + e.getMessage()); 17 | } 18 | } 19 | 20 | private static int complexDataProcessing(Map> dataMap) throws Exception { 21 | int result = 0; 22 | 23 | for (Map.Entry> entry : dataMap.entrySet()) { 24 | String key = entry.getKey(); 25 | for (Integer value : entry.getValue()) { 26 | if (key.startsWith("F")) { 27 | result += complexCalculation(value, v -> v * v); 28 | } else { 29 | result -= complexCalculation(value, v -> v + v); 30 | } 31 | } 32 | } 33 | 34 | Optional max = dataMap.values().stream() 35 | .flatMap(Collection::stream) 36 | .max(Integer::compare); 37 | 38 | 39 | final int finalResult = result; 40 | 41 | return max.map(m -> finalResult * m).orElseThrow(() -> new RuntimeException("No maximum value found")); 42 | } 43 | 44 | private static int complexCalculation(Integer value, Function function) { 45 | return Optional.ofNullable(value) 46 | .map(function) 47 | .orElseThrow(() -> new IllegalArgumentException("Value cannot be null")); 48 | } 49 | } -------------------------------------------------------------------------------- /tests/data/AnnotationTest.java: -------------------------------------------------------------------------------- 1 | import java.lang.annotation.ElementType; 2 | import java.lang.annotation.Retention; 3 | import java.lang.annotation.RetentionPolicy; 4 | import java.lang.annotation.Target; 5 | import java.util.*; 6 | import java.util.function.Supplier; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.TYPE_USE, ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.FIELD}) 10 | @interface Anno { 11 | String value(); 12 | } 13 | 14 | @Anno("Class Definition") 15 | public class AnnotationTest<@Anno("Type Parameter") T extends @Anno("Type Parameter Super")ArrayList<@Anno("Type Parameter Type Annotation") String>> implements Cloneable, @Anno("Interface")Supplier> { 16 | 17 | @Anno("Field") 18 | private final T example; 19 | 20 | @Anno("Constructor") 21 | public AnnotationTest(@Anno("Constructor Argument") T example) { 22 | this.example = example; 23 | } 24 | 25 | @Anno("Method") 26 | @SuppressWarnings("unchecked") 27 | public T get(@Anno("Method Argument") int arg) throws @Anno("Throws") Exception { 28 | @Anno("Local Variable") ArrayList provided = get(); 29 | if (provided == null) { 30 | throw new @Anno("Exception Type") Exception("Provided string is null"); 31 | } 32 | return (@Anno("Local Cast Type") T) provided; 33 | } 34 | 35 | @Override 36 | public ArrayList get() { 37 | return new ArrayList<>(); 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | if (o instanceof @Anno("Instanceof Type") AnnotationTest) 43 | return Objects.equals(example, ((@Anno("Generic Cast Type") AnnotationTest) o).example); 44 | return false; 45 | } 46 | 47 | @Override 48 | public @Anno("Return Type") AnnotationTest<@Anno("Generic Parameter") T> clone() throws @Anno("Clone Throws") CloneNotSupportedException { 49 | return (AnnotationTest<@Anno("Local Generic Cast") T>) super.clone(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hippocafe 2 | 3 | A java bytecode manipulation library written in C++. 4 | 5 | ## Building 6 | ```shell 7 | mkdir build 8 | cd build 9 | cmake .. 10 | cmake --build . --config Release 11 | cmake --install . --config Release 12 | ``` 13 | 14 | ## Example Usage 15 | ```cpp 16 | #include 17 | 18 | using namespace cafe; 19 | 20 | // The source of the class file, can either be a std::vector or std::istream. 21 | std::ifstream source("data/HelloWorld.class", std::ios::binary); 22 | 23 | class_reader reader; 24 | result read_result = reader.read(source); 25 | if (!read_result) { 26 | std::cerr << read_result.err().message() << std::endl; 27 | return; 28 | } 29 | class_file& file = read_result.value(); 30 | 31 | for (auto& method : file.methods) { 32 | for (auto& insn : method.code) { 33 | if (auto* push = std::get_if(&insn)) { 34 | if (push->operand == value{"Hello World"}) { 35 | push->operand = "Modified"; 36 | } 37 | } 38 | } 39 | } 40 | 41 | class_writer writer; 42 | std::vector data = writer.write(file); 43 | std::ofstream os("HelloWorld.class", std::ios::binary); 44 | os.write(reinterpret_cast(data.data()), data.size()); 45 | ``` 46 | 47 | ## Class Writer Flags 48 | hippocafe's class writer can be configured to automatically compute the method maxes (max stack and max locals) and/or stack map frames. 49 | In order to compute maxes, simply pass `cafe::compute_maxes` to your `cafe::class_writer` 50 | ```cpp 51 | cafe::class_writer writer(cafe::compute_maxes); 52 | ``` 53 | To compute frames you must create a `cafe::class_tree`, then pass it to the class writer as such. 54 | ```cpp 55 | cafe::class_tree tree(cafe::load_rt); 56 | cafe::class_writer writer(tree); 57 | ``` 58 | For more accurate frame generation, you can add class information to the class tree. 59 | ```cpp 60 | tree.put("my/ThisClass", "my/SuperClass", {"my/Interface1", "my/Interface2"}); 61 | // or 62 | cafe::class_file file = ...; 63 | tree.put(file); 64 | ``` -------------------------------------------------------------------------------- /include/cafe/class_tree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "apidef.hpp" 11 | #include "class_file.hpp" 12 | 13 | namespace cafe { 14 | 15 | struct CAFE_API load_rt_t {}; 16 | inline constexpr load_rt_t load_rt{}; 17 | 18 | class CAFE_API class_tree { 19 | public: 20 | class node { 21 | public: 22 | std::string name; 23 | node* super_class; 24 | std::vector interfaces; 25 | explicit node(const std::string_view& name); 26 | node(const std::string_view& name, node* super_class, const std::vector& interfaces); 27 | ~node() = default; 28 | node(const node&) = default; 29 | node(node&&) noexcept = default; 30 | node& operator=(const node&) = default; 31 | node& operator=(node&&) noexcept = default; 32 | }; 33 | using map_type = std::unordered_map>; 34 | 35 | class_tree() = default; 36 | class_tree(load_rt_t); 37 | ~class_tree() = default; 38 | class_tree(const class_tree&) = delete; 39 | class_tree(class_tree&&) noexcept = default; 40 | class_tree& operator=(const class_tree&) = delete; 41 | class_tree& operator=(class_tree&&) noexcept = default; 42 | 43 | node* get_or_create(const std::string& name); 44 | void put(const std::string& name, const std::optional& super_name, const std::vector& interfaces); 45 | void put(const class_file& file); 46 | template 47 | void put_all(const Iterator& it) { 48 | for (const auto& file : it) { 49 | put(file); 50 | } 51 | } 52 | node* get(const std::string& name) const; 53 | bool is_assignable_from(const std::string& from, const std::string& to) const; 54 | size_t size() const; 55 | map_type::const_iterator begin() const; 56 | map_type::const_iterator end() const; 57 | map_type::iterator begin(); 58 | map_type::iterator end(); 59 | bool empty() const; 60 | 61 | private: 62 | map_type tree_; 63 | 64 | bool is_primitive(const std::string_view& name) const; 65 | }; 66 | 67 | } -------------------------------------------------------------------------------- /tests/class_writer_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | TEST(class_writer, test) { 9 | std::string test_name = "ForLoopTest"; 10 | std::ifstream stream(std::string("data/") + test_name + ".class", std::ios::binary); 11 | 12 | cafe::class_reader reader; 13 | const auto file_res = reader.read(stream); 14 | if (!file_res) { 15 | std::cerr << file_res.err().message() << std::endl; 16 | return; 17 | } 18 | const auto& file = file_res.value(); 19 | 20 | for (const auto& anno : file.visible_annotations) { 21 | std::cout << "Visible annotation: " << anno.to_string() << std::endl; 22 | } 23 | 24 | cafe::class_writer writer; 25 | std::ofstream os(test_name + ".class", std::ios::binary); 26 | const auto data = writer.write(file); 27 | os.write(reinterpret_cast(data.data()), data.size()); 28 | os.close(); 29 | 30 | for (const auto& method : file.methods) { 31 | std::cout << method.name_desc() << std::endl; 32 | for (const auto& insn : method.body) { 33 | std::cout << " " << cafe::to_string(insn) << std::endl; 34 | } 35 | for (const auto& [label, frame] : method.body.frames) { 36 | std::cout << " > " << label.to_string() << " " << cafe::to_string(frame) << std::endl; 37 | } 38 | } 39 | 40 | std::cout << "read2: " << std::endl << std::endl; 41 | 42 | cafe::class_reader reader2; 43 | const auto file2_res = reader2.read(data); 44 | if (!file2_res) { 45 | std::cerr << file2_res.err().message() << std::endl; 46 | return; 47 | } 48 | const auto& file2 = file2_res.value(); 49 | 50 | for (const auto& anno : file2.visible_annotations) { 51 | std::cout << "Visible annotation: " << anno.to_string() << std::endl; 52 | } 53 | 54 | for (const auto& method : file2.methods) { 55 | 56 | std::cout << method.name_desc() << std::endl; 57 | for (const auto& insn : method.body) { 58 | std::cout << " " << cafe::to_string(insn) << std::endl; 59 | } 60 | for (const auto& [label, frame] : method.body.frames) { 61 | std::cout << " > " << label.to_string() << " " << cafe::to_string(frame) << std::endl; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /include/cafe/class_reader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "constant_pool.hpp" 6 | #include "value.hpp" 7 | #include "data_reader.hpp" 8 | #include "class_file.hpp" 9 | 10 | namespace cafe { 11 | 12 | class CAFE_API class_reader { 13 | public: 14 | class_reader() = default; 15 | ~class_reader() = default; 16 | class_reader(const class_reader&) = delete; 17 | class_reader& operator=(const class_reader&) = delete; 18 | class_reader(class_reader&&) noexcept = default; 19 | class_reader& operator=(class_reader&&) noexcept = default; 20 | 21 | result read(data_reader&& reader); 22 | result read(data_reader&& reader, class_file& file); 23 | 24 | private: 25 | data_reader reader_{{}}; 26 | cp::constant_pool pool_; 27 | std::vector bootstrap_methods_; 28 | uint32_t class_version_{}; 29 | size_t label_count_ = 0; 30 | 31 | result read_code(code& code, uint32_t code_length); 32 | result read_method(class_file& file); 33 | result read_field(class_file& file); 34 | result read_record(class_file& file); 35 | result read_header(); 36 | result read_annotation(); 37 | result read_type_annotation(std::vector>& labels); 38 | result read_element_value(); 39 | result read_frame_var(std::vector>& labels); 40 | label get_label(std::vector>& labels, size_t offset); 41 | std::optional