From a87b0f8976b4f657609078a274ef96f195ae4a1a Mon Sep 17 00:00:00 2001 From: Robert Obkircher Date: Mon, 31 May 2021 11:04:49 +0200 Subject: [PATCH 1/4] download OpenJDK 11 --- .idea/misc.xml | 2 ++ README.md | 4 ++++ jdk/.gitignore | 2 ++ jdk/download.sh | 23 +++++++++++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 jdk/.gitignore create mode 100755 jdk/download.sh diff --git a/.idea/misc.xml b/.idea/misc.xml index e8c163d..bdf7065 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,6 +6,8 @@ + + diff --git a/README.md b/README.md index dcc13e9..e5137b7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ A Java Bytecode interpreter. [![Run tests](https://github.com/RobertObkircher/SchokoVM/actions/workflows/tests.yml/badge.svg)](https://github.com/RobertObkircher/SchokoVM/actions/workflows/tests.yml) +# OpenJDK + +You need to download an OpenJDK release and the sources: `cd jdk && ./download.sh` + # GDB You need a ~/.gdbinit in your home directory or the project .gdbinit file will be ignored: diff --git a/jdk/.gitignore b/jdk/.gitignore new file mode 100644 index 0000000..e74db6a --- /dev/null +++ b/jdk/.gitignore @@ -0,0 +1,2 @@ +/jdk-11.0.11+9/ +/jdk/ \ No newline at end of file diff --git a/jdk/download.sh b/jdk/download.sh new file mode 100755 index 0000000..ea33a72 --- /dev/null +++ b/jdk/download.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +set -e + +if [ -d "jdk-11.0.11+9" ]; then + echo "Skipping download of jdk release" +else + ARCHIVE="OpenJDK11U-jdk_x64_linux_hotspot_11.0.11_9.tar.gz" + wget "https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.11%2B9/$ARCHIVE" + tar -xf "$ARCHIVE" + rm "$ARCHIVE" +fi + +if [ -d "jdk" ]; then + echo "Skipping download of jdk sources" +else + git clone https://github.com/openjdk/jdk.git + pushd jdk + git checkout -b SchokoVM jdk-11+9 + popd +fi + + From 5d02752dd13bdac3cadcedb1e6e7b28983040ce6 Mon Sep 17 00:00:00 2001 From: Robert Obkircher Date: Mon, 31 May 2021 12:27:47 +0200 Subject: [PATCH 2/4] refactored reading of zip entries --- src/zip.cpp | 80 ++++++++++++++++++++++------------------------------- src/zip.hpp | 6 ++-- 2 files changed, 37 insertions(+), 49 deletions(-) diff --git a/src/zip.cpp b/src/zip.cpp index 6a619c5..ddb9944 100644 --- a/src/zip.cpp +++ b/src/zip.cpp @@ -2,9 +2,7 @@ #include #include -#include "classfile.hpp" #include "zip.hpp" -#include "parser.hpp" ZipException::ZipException(std::string message) : message(std::move(message)) {} @@ -12,51 +10,6 @@ const char *ZipException::what() const noexcept { return message.c_str(); } -// https://stackoverflow.com/a/7782037 -struct membuf : std::streambuf { - membuf(char *begin, char *end) { - this->setg(begin, begin, end); - } -}; - -void read_entire_jar(const char *path, std::vector &buffer, std::vector &class_files) { - ZipArchive zip{path}; - - for (const auto &item : zip.entries) { - auto const &name = item.first; - auto const &entry = item.second; - - if (!name.ends_with(".class")) { - continue; - } - - if (buffer.size() < entry.size) { - buffer.resize(std::max((size_t) entry.size, buffer.size() * 2)); - } - - zip_file_t *file = zip_fopen_index(zip.archive.get(), entry.index, 0); - if (file == nullptr) { - throw ZipException("zip_fopen_index"); - } - - zip_int64_t length = zip_fread(file, buffer.data(), buffer.size()); - - if (zip_fclose(file) != 0) { - throw ZipException("zip_fclose failed (zip_fread length was " + std::to_string(length) + ")"); - } - - if (length == -1) { - throw ZipException("zip_fread"); - } - - // TODO maybe the parser should simply use a buffer instead of istream? - membuf buf{buffer.data(), buffer.data() + length}; - std::istream in{&buf}; - Parser parser{in}; - class_files.push_back(parser.parse()); - } -} - ZipArchive::ZipArchive() : archive(), path(), entries() { } @@ -85,3 +38,36 @@ ZipArchive::ZipArchive(std::string path) : archive(), path(std::move(path)), ent entries.insert({{stat.name}, {index, stat.size}}); } } + +ZipEntry const *ZipArchive::entry_for_path(std::string &filepath) const { + if (auto found = entries.find(filepath); found != entries.end()) { + return &found->second; + } else { + return nullptr; + } +} + +void ZipArchive::read(ZipEntry const &entry, std::vector &buffer) const { + buffer.resize(entry.size); + + zip_file_t *file = zip_fopen_index(archive.get(), entry.index, 0); + if (file == nullptr) { + throw ZipException("zip_fopen_index"); + } + + zip_int64_t length = zip_fread(file, buffer.data(), buffer.size()); + + if (zip_fclose(file) != 0) { + throw ZipException("zip_fclose failed (zip_fread length was " + std::to_string(length) + ")"); + } + + if (length == -1) { + throw ZipException("zip_fread failed"); + } + + if (static_cast(length) != entry.size) { + throw ZipException("zip_fread: length was " + std::to_string(length) + " but the expected size was " + + std::to_string(entry.size)); + } +} + diff --git a/src/zip.hpp b/src/zip.hpp index ae7c786..0ce4ca1 100644 --- a/src/zip.hpp +++ b/src/zip.hpp @@ -5,8 +5,6 @@ #include #include -void read_entire_jar(const char *path, std::vector &buffer, std::vector &class_files); - struct ZipException : std::exception { std::string message; @@ -34,6 +32,10 @@ struct ZipArchive { ZipArchive(); explicit ZipArchive(std::string path); + + ZipEntry const *entry_for_path(std::string &filepath) const; + + void read(ZipEntry const &entry, std::vector &buffer) const; }; #endif //SCHOKOVM_ZIP_HPP From 31b787753aa9a16ca5bed8a1c4a7881c65e5e6b3 Mon Sep 17 00:00:00 2001 From: Robert Obkircher Date: Mon, 31 May 2021 14:55:24 +0200 Subject: [PATCH 3/4] BootstrapClassLoader + lazy class loading --- .idea/misc.xml | 1 + jdk/.gitignore | 3 +- jdk/download.sh | 2 + src/classfile.hpp | 14 +++++-- src/classloading.cpp | 96 ++++++++++++++++++++++++++++++++++++++------ src/classloading.hpp | 25 +++++++++++- src/interpreter.cpp | 43 ++++++++++---------- src/interpreter.hpp | 4 +- src/main.cpp | 54 +++---------------------- src/parser.cpp | 12 +++--- 10 files changed, 158 insertions(+), 96 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index bdf7065..4444df8 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,6 +6,7 @@ + diff --git a/jdk/.gitignore b/jdk/.gitignore index e74db6a..4752f89 100644 --- a/jdk/.gitignore +++ b/jdk/.gitignore @@ -1,2 +1,3 @@ +/exploded-modules/ +/jdk/ /jdk-11.0.11+9/ -/jdk/ \ No newline at end of file diff --git a/jdk/download.sh b/jdk/download.sh index ea33a72..963f766 100755 --- a/jdk/download.sh +++ b/jdk/download.sh @@ -9,6 +9,8 @@ else wget "https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.11%2B9/$ARCHIVE" tar -xf "$ARCHIVE" rm "$ARCHIVE" + + jimage extract --dir exploded-modules jdk-11.0.11+9/lib/modules fi if [ -d "jdk" ]; then diff --git a/src/classfile.hpp b/src/classfile.hpp index 1752962..c53a4b0 100644 --- a/src/classfile.hpp +++ b/src/classfile.hpp @@ -782,7 +782,9 @@ struct ClassFile { u2 access_flags; CONSTANT_Class_info *this_class; // nullptr for class Object - CONSTANT_Class_info *super_class; + CONSTANT_Class_info *super_class_ref; + // nullptr for class Object and before resolution + ClassFile *super_class; std::vector interfaces; std::vector fields; std::vector methods; @@ -799,17 +801,19 @@ struct ClassFile { // Computes whether `this` is a subclass of `other` (regarding both `extends` and `implements`). // Note that `x.is_subclass_of(x) == false` bool is_subclass_of(ClassFile *other) { + if (this->super_class == other) { + return true; + } for (auto &i: interfaces) { if (i->clazz == other) { return true; } } - if (this->super_class != nullptr && - (this->super_class->clazz == other || this->super_class->clazz->is_subclass_of(other))) { + if (this->super_class != nullptr && this->super_class->is_subclass_of(other)) { return true; } for (auto &i: interfaces) { - if (i->clazz == other || i->clazz->is_subclass_of(other)) { + if (i->clazz->is_subclass_of(other)) { return true; } } @@ -819,6 +823,8 @@ struct ClassFile { [[nodiscard]] inline bool is_interface() const { return (access_flags & static_cast(ClassFileAccessFlags::ACC_INTERFACE)) != 0; } + + [[nodiscard]] inline std::string const &name() const { return this_class->name->value; } }; diff --git a/src/classloading.cpp b/src/classloading.cpp index 3c9d84d..927367c 100644 --- a/src/classloading.cpp +++ b/src/classloading.cpp @@ -1,4 +1,76 @@ +#include +#include + #include "classloading.hpp" +#include "parser.hpp" +#include "util.hpp" +#include "zip.hpp" + +BootstrapClassLoader::BootstrapClassLoader(const std::string &bootclasspath) : class_path_entries(), buffer() { + // TODO fix hardcoded path + class_path_entries.push_back({"java.base", {}, "../jdk/exploded-modules/java.base", {}}); + + for (auto &path : split(bootclasspath, ':')) { + if (path.ends_with(".jar") || path.ends_with(".zip")) { + class_path_entries.push_back({"", {}, "", ZipArchive(path)}); + } else { + class_path_entries.push_back({path, {}, path, {}}); + } + } +} + +// https://stackoverflow.com/a/7782037 +struct membuf : std::streambuf { + membuf(char *begin, char *end) { + this->setg(begin, begin, end); + } +}; + +ClassFile *BootstrapClassLoader::load(std::string const &name) { + for (auto &cp_entry : class_path_entries) { + if (auto loaded = cp_entry.class_files.find(name); loaded != cp_entry.class_files.end()) { + if (loaded->second == nullptr) { + continue; + } + return loaded->second.get(); + } + + std::unique_ptr parsed{}; + + if (!cp_entry.directory.empty()) { + auto path = cp_entry.directory + "/" + name + ".class"; + std::ifstream in{path, std::ios::in | std::ios::binary}; + + if (in) { + Parser parser{in}; + parsed = std::make_unique(parser.parse()); + } + } else if (!cp_entry.zip.path.empty()) { + auto path = name + ".class"; + + if (ZipEntry const *zip_entry = cp_entry.zip.entry_for_path(path); zip_entry != nullptr) { + cp_entry.zip.read(*zip_entry, buffer); + + membuf buf{buffer.data(), buffer.data() + buffer.size()}; + std::istream in{&buf}; + + Parser parser{in}; + parsed = std::make_unique(parser.parse()); + } + } + + ClassFile *result = parsed.get(); + cp_entry.class_files.insert({name, std::move(parsed)}); + + if (result != nullptr) { + if (name != result->name()) { + throw ParseError("unexpected name"); + } + return result; + } + } + return nullptr; +} static bool initialize_class(ClassFile *clazz, Thread &thread, Frame &frame) { for (const auto &field : clazz->fields) { @@ -52,7 +124,7 @@ static bool initialize_class(ClassFile *clazz, Thread &thread, Frame &frame) { } -bool resolve_class(std::unordered_map &class_files, CONSTANT_Class_info *class_info, +bool resolve_class(BootstrapClassLoader &bootstrap_class_loader, CONSTANT_Class_info *class_info, Thread &thread, Frame &frame) { if (class_info->clazz == nullptr) { auto &name = class_info->name->value; @@ -62,12 +134,11 @@ bool resolve_class(std::unordered_map &class_file // TODO we also need to deal with array classes here. They also load the class for the elements. - auto result = class_files.find(name); - if (result == class_files.end()) { + ClassFile *clazz = bootstrap_class_loader.load(name); + if (clazz == nullptr) { // TODO this prints "A not found" if A was found but a superclass/interface wasn't throw std::runtime_error("class not found: '" + name + "'"); } - ClassFile *clazz = result->second; if (clazz->resolved) { class_info->clazz = clazz; @@ -80,13 +151,12 @@ bool resolve_class(std::unordered_map &class_file } size_t parent_instance_field_count = 0; - // TODO use stdlib - if (clazz->super_class != nullptr && clazz->super_class->name->value != "java/lang/Object" && - clazz->super_class->name->value != "java/lang/Exception") { - auto runInitializer = resolve_class(class_files, clazz->super_class, thread, frame); - if (runInitializer) - return runInitializer; - parent_instance_field_count = clazz->super_class->clazz->total_instance_field_count; + if (clazz->super_class == nullptr && clazz->super_class_ref != nullptr) { + if (resolve_class(bootstrap_class_loader, clazz->super_class_ref, thread, frame)) + return true; + + clazz->super_class = clazz->super_class_ref->clazz; + parent_instance_field_count = clazz->super_class->total_instance_field_count; } // instance and static fields @@ -105,7 +175,7 @@ bool resolve_class(std::unordered_map &class_file clazz->static_field_values.resize(clazz->fields.size() - clazz->declared_instance_field_count); for (auto &interface : clazz->interfaces) { - auto runInitializer = resolve_class(class_files, interface, thread, frame); + auto runInitializer = resolve_class(bootstrap_class_loader, interface, thread, frame); if (runInitializer) return true; } @@ -150,7 +220,7 @@ bool resolve_field_recursive(ClassFile *clazz, CONSTANT_Fieldref_info *field_inf if (clazz->super_class != nullptr) { // 3. - clazz = clazz->super_class->clazz; + clazz = clazz->super_class; } else { break; } diff --git a/src/classloading.hpp b/src/classloading.hpp index 65f2ce6..f16f7b8 100644 --- a/src/classloading.hpp +++ b/src/classloading.hpp @@ -1,15 +1,38 @@ #ifndef SCHOKOVM_CLASSLOADING_HPP #define SCHOKOVM_CLASSLOADING_HPP +#include #include + #include "classfile.hpp" #include "interpreter.hpp" +#include "zip.hpp" + +struct ClassPathEntry { + std::string module_name; + std::unordered_map> class_files; + + // TODO use subtyping instead? + // TODO should module entries refer to other entries? + // one of those will be empty + std::string directory; + ZipArchive zip; +}; + +struct BootstrapClassLoader { + std::vector class_path_entries; + std::vector buffer; + + explicit BootstrapClassLoader(const std::string &bootclasspath); + + ClassFile *load(std::string const &name); +}; /** * Throws if the class was not found * @return whether a stack frame for an initializer was pushed */ -bool resolve_class(std::unordered_map &class_files, CONSTANT_Class_info *class_info, +bool resolve_class(BootstrapClassLoader &bootstrap_class_loader, CONSTANT_Class_info *class_info, Thread &thread, Frame &frame); bool resolve_field_recursive(ClassFile *clazz, CONSTANT_Fieldref_info *field_info); diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 3a1ab4b..9ca67d0 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -27,7 +27,7 @@ enum class ArrayPrimitiveTypes { }; static void execute_instruction(Heap &heap, Thread &thread, Frame &frame, - std::unordered_map &class_files, bool &shouldExit); + BootstrapClassLoader &bootstrap_class_loader, bool &shouldExit); static void execute_comparison(Frame &frame, bool condition); @@ -51,7 +51,7 @@ void fill_multi_array(Heap &heap, Reference &reference, const std::span &cou method_selection(ClassFile *dynamic_class, ClassFile *declared_class, method_info *declared_method, ClassFile *&out_class, method_info *&out_method); -int interpret(std::unordered_map &class_files, ClassFile *main) { +int interpret(BootstrapClassLoader &bootstrap_class_loader, ClassFile *main) { auto main_method_iter = std::find_if(main->methods.begin(), main->methods.end(), [](const method_info &m) { return m.name_index->value == MAIN_NAME && @@ -75,11 +75,11 @@ int interpret(std::unordered_map &class_files, Cl Frame frame{thread.stack, main, main_method, thread.stack.memory_used}; // push the class initializer if necessary - resolve_class(class_files, main->this_class, thread, frame); + resolve_class(bootstrap_class_loader, main->this_class, thread, frame); bool shouldExit = false; while (!shouldExit) { - execute_instruction(heap, thread, frame, class_files, shouldExit); + execute_instruction(heap, thread, frame, bootstrap_class_loader, shouldExit); } // print exit code @@ -91,7 +91,7 @@ int interpret(std::unordered_map &class_files, Cl } static inline void execute_instruction(Heap &heap, Thread &thread, Frame &frame, - std::unordered_map &class_files, + BootstrapClassLoader &bootstrap_class_loader, bool &shouldExit) { std::vector &code = *frame.code; auto opcode = code[frame.pc]; @@ -873,7 +873,7 @@ static inline void execute_instruction(Heap &heap, Thread &thread, Frame &frame, auto field = frame.clazz->constant_pool.get(index); if (!field.resolved) { - if (resolve_class(class_files, field.class_, thread, frame)) { + if (resolve_class(bootstrap_class_loader, field.class_, thread, frame)) { return; } @@ -957,7 +957,7 @@ static inline void execute_instruction(Heap &heap, Thread &thread, Frame &frame, method_info *declared_method = declared_method_ref.method; if (declared_method == nullptr) { - if (resolve_class(class_files, declared_method_ref.class_, thread, frame)) { + if (resolve_class(bootstrap_class_loader, declared_method_ref.class_, thread, frame)) { return; } @@ -1002,7 +1002,7 @@ static inline void execute_instruction(Heap &heap, Thread &thread, Frame &frame, ClassFile *clazz = method_ref->class_->clazz; if (method == nullptr) { - if (resolve_class(class_files, method_ref->class_, thread, frame)) { + if (resolve_class(bootstrap_class_loader, method_ref->class_, thread, frame)) { return; } @@ -1101,7 +1101,7 @@ static inline void execute_instruction(Heap &heap, Thread &thread, Frame &frame, ClassFile *clazz = method_ref->class_->clazz; if (method == nullptr) { - if (resolve_class(class_files, method_ref->class_, thread, frame)) { + if (resolve_class(bootstrap_class_loader, method_ref->class_, thread, frame)) { return; } @@ -1114,7 +1114,9 @@ static inline void execute_instruction(Heap &heap, Thread &thread, Frame &frame, } if (method->is_native()) { - abort(); + if (method->name_index->value != "registerNatives") { + abort(); + } frame.pc += 3; return; } else { @@ -1130,7 +1132,7 @@ static inline void execute_instruction(Heap &heap, Thread &thread, Frame &frame, method_info *declared_method = declared_method_ref.method; if (declared_method == nullptr) { - if (resolve_class(class_files, declared_method_ref.class_, thread, frame)) { + if (resolve_class(bootstrap_class_loader, declared_method_ref.class_, thread, frame)) { return; } @@ -1159,7 +1161,7 @@ static inline void execute_instruction(Heap &heap, Thread &thread, Frame &frame, u2 index = frame.read_u2(); auto &class_info = frame.clazz->constant_pool.get(index); - if (resolve_class(class_files, &class_info, thread, frame)) { + if (resolve_class(bootstrap_class_loader, &class_info, thread, frame)) { return; } frame.pc += 2; @@ -1399,8 +1401,8 @@ static void handle_throw(Thread &thread, Frame &frame, bool &shouldExit, Referen // but without running the class initializer. auto &clazz_name = frame.clazz->constant_pool.get( e.catch_type).name->value; - for (ClassFile *c = obj->clazz;; c = c->super_class->clazz) { - if (c->this_class->name->value == clazz_name) { return true; } + for (ClassFile *c = obj->clazz;; c = c->super_class) { + if (c->name() == clazz_name) { return true; } if (c->super_class == nullptr) { break; } } return false; @@ -1415,11 +1417,11 @@ static void handle_throw(Thread &thread, Frame &frame, bool &shouldExit, Referen // Bubbled to the top, no exception handler was found, so exit thread std::string message = "Exception in thread \"main\" "; - message.append(obj->clazz->this_class->name->value); + message.append(obj->clazz->name()); message.append("\n"); for (const Frame &f : stack_trace) { message.append("\tat "); - message.append(f.clazz->this_class->name->value); + message.append(f.clazz->name()); message.append("."); message.append(f.method->name_index->value); @@ -1586,7 +1588,7 @@ resolve_method_interfaces(ClassFile *clazz, const std::string &name, const std:: out_clazz_fallback, out_method_fallback); } if (clazz->super_class) { - resolve_method_interfaces(clazz->super_class->clazz, name, descriptor, + resolve_method_interfaces(clazz->super_class, name, descriptor, out_clazz_max_specific, out_method_max_specific, out_clazz_fallback, out_method_fallback); } @@ -1598,7 +1600,7 @@ resolve_method_interfaces(ClassFile *clazz, const std::string &name, const std:: // https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-5.html#jvms-5.4.3.3 // 2. - for (ClassFile *c = method.class_->clazz;; c = c->super_class->clazz) { + for (ClassFile *c = method.class_->clazz; c != nullptr; c = c->super_class) { for (auto &m : c->methods) { if (m.name_index->value == name && m.descriptor_index->value == descriptor) { @@ -1606,7 +1608,6 @@ resolve_method_interfaces(ClassFile *clazz, const std::string &name, const std:: return false; } } - if (c->super_class == nullptr) { break; } } // 3. @@ -1645,7 +1646,7 @@ method_selection(ClassFile *dynamic_class, ClassFile *declared_class, method_inf const auto &name = declared_method->name_index->value; const auto &descriptor = declared_method->descriptor_index->value; // 2. (1 + 2) - for (ClassFile *clazz = dynamic_class;; clazz = clazz->super_class->clazz) { + for (ClassFile *clazz = dynamic_class; clazz != nullptr; clazz = clazz->super_class) { for (auto &m : clazz->methods) { // "can override" according to ยง5.4.5 if (m.name_index->value == name && @@ -1659,8 +1660,6 @@ method_selection(ClassFile *dynamic_class, ClassFile *declared_class, method_inf return false; } } - - if (clazz->super_class == nullptr) { break; } } // 2. (3) diff --git a/src/interpreter.hpp b/src/interpreter.hpp index 0121637..489852d 100644 --- a/src/interpreter.hpp +++ b/src/interpreter.hpp @@ -235,6 +235,8 @@ struct Thread { Stack stack{}; }; -int interpret(std::unordered_map &class_files, ClassFile *main); +struct BootstrapClassLoader; + +int interpret(BootstrapClassLoader &bootstrap_class_loader, ClassFile *main); #endif //SCHOKOVM_INTERPRETER_HPP diff --git a/src/main.cpp b/src/main.cpp index fb71d51..1846ce0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,63 +1,21 @@ #include #include -#include #include "args.hpp" -#include "classfile.hpp" -#include "zip.hpp" +#include "classloading.hpp" #include "interpreter.hpp" -#include "parser.hpp" -#include "util.hpp" int run(const Arguments &arguments) { - std::vector class_files_list; + // todo: we use the classpath as bootstrap classpath as a hack + BootstrapClassLoader bootstrap_class_loader{arguments.classpath}; - { - std::vector buffer; - buffer.resize(1024); - - for (auto &path : split(arguments.classpath, ':')) { - if (path.ends_with(".jar") || path.ends_with(".zip")) { - try { - read_entire_jar(path.c_str(), buffer, class_files_list); - } catch (ZipException &e) { - std::cerr << "Could not read jar file " << path << " error: " << e.what() << "\n"; - return 23; - } - } else { - for (const auto &directory_entry : std::filesystem::recursive_directory_iterator(path)) { - if (directory_entry.is_regular_file() && directory_entry.path().extension() == ".class") { - std::ifstream in{directory_entry.path(), std::ios::in | std::ios::binary}; - if (in) { - Parser parser{in}; - class_files_list.push_back(parser.parse()); - } else { - std::cerr << "Failed to read file\n"; - return -2; - } - } - } - } - } - } - - std::unordered_map class_files; - for (auto &clazz : class_files_list) { - std::string_view name{clazz.this_class->name->value}; - auto pair = class_files.insert({name, &clazz}); - if (!pair.second) { - std::cerr << "The class '" << name << "' was found twice!\n"; - return 84; - } - } - - auto main_class = class_files.find(arguments.mainclass); - if (main_class == class_files.end()) { + auto main_class = bootstrap_class_loader.load(arguments.mainclass); + if (main_class == nullptr) { std::cerr << "mainclass was not found!\n"; return 5; } - return interpret(class_files, main_class->second); + return interpret(bootstrap_class_loader, main_class); } int main(int argc, char *argv[]) { diff --git a/src/parser.cpp b/src/parser.cpp index f73761b..3f30528 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -41,14 +41,14 @@ ClassFile Parser::parse() { u2 super_class = eat_u2(); if (super_class == 0) { // class Object - result.super_class = nullptr; + result.super_class_ref = nullptr; } else { - result.super_class = &check_cp_range_and_type(result.constant_pool, super_class); - if (result.super_class->name->value == "java/lang/Object" || - // TODO remove once we have a stdlib - result.super_class->name->value == "java/lang/Exception") - result.super_class = nullptr; + result.super_class_ref = &check_cp_range_and_type(result.constant_pool, super_class); + // TODO remove once we have strings + if (result.super_class_ref->name->value == "java/lang/Exception") + result.super_class_ref = nullptr; } + result.super_class = nullptr; // resolved later u2 interfaces_count = eat_u2(); for (int i = 0; i < interfaces_count; ++i) From e90ef9f85a04dd84a53fc4805b86b0a492229939 Mon Sep 17 00:00:00 2001 From: Robert Obkircher Date: Mon, 31 May 2021 19:36:13 +0200 Subject: [PATCH 4/4] explode modules using cmake --- .idea/misc.xml | 2 +- CMakeLists.txt | 28 +++++++++++++++++++++++++++- jdk/.gitignore | 4 ++-- jdk/cleanup.sh | 6 ++++++ jdk/download.sh | 12 ------------ src/zip.cpp | 2 +- src/zip.hpp | 3 ++- 7 files changed, 39 insertions(+), 18 deletions(-) create mode 100755 jdk/cleanup.sh diff --git a/.idea/misc.xml b/.idea/misc.xml index 4444df8..a9010cc 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,9 +6,9 @@ + - diff --git a/CMakeLists.txt b/CMakeLists.txt index 77313a6..1baa15e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,8 @@ project(SchokoVM) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True) -find_package(Java 1.8 REQUIRED) +find_package(Java 11 EXACT REQUIRED COMPONENTS Runtime Development) +find_package(JNI 11 EXACT REQUIRED) include(UseJava) find_package(libzip) @@ -102,6 +103,31 @@ else () target_link_libraries(SchokoVM zip) endif () + +# +# OpenJDK +# + +file(REAL_PATH ${Java_JAVAC_EXECUTABLE} JDK_HOME) +get_filename_component(JDK_HOME ${JDK_HOME} DIRECTORY) +get_filename_component(JDK_HOME ${JDK_HOME} DIRECTORY) +message(STATUS "jdk: ${JDK_HOME}") + +set(JDK_BIN ${CMAKE_CURRENT_SOURCE_DIR}/jdk/bin) +if(NOT IS_DIRECTORY ${JDK_BIN}) + message(STATUS "Copying ${JDK_HOME} to ${JDK_BIN}") +# file(COPY ${JDK_HOME} DESTINATION ${JDK_BIN}) + # this renames the target folder: + execute_process(COMMAND cp -r "${JDK_HOME}" "${JDK_BIN}") +endif() + +set(JDK_EXPLODED ${CMAKE_CURRENT_SOURCE_DIR}/jdk/exploded-modules) +if(NOT IS_DIRECTORY ${JDK_EXPLODED}) + message(STATUS "Exploding") + execute_process(COMMAND ${JDK_HOME}/bin/jimage extract --dir "${JDK_EXPLODED}" "${JDK_BIN}/lib/modules") +endif() + + # # Tests # diff --git a/jdk/.gitignore b/jdk/.gitignore index 4752f89..c4b6a46 100644 --- a/jdk/.gitignore +++ b/jdk/.gitignore @@ -1,3 +1,3 @@ +/bin/ /exploded-modules/ -/jdk/ -/jdk-11.0.11+9/ +/jdk/ \ No newline at end of file diff --git a/jdk/cleanup.sh b/jdk/cleanup.sh new file mode 100755 index 0000000..93cf6de --- /dev/null +++ b/jdk/cleanup.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -e + +# clion doesn't want to delete the symlinks +rm -rf bin +rm -rf exploded-modules diff --git a/jdk/download.sh b/jdk/download.sh index 963f766..2117f0b 100755 --- a/jdk/download.sh +++ b/jdk/download.sh @@ -1,18 +1,6 @@ #!/bin/sh - set -e -if [ -d "jdk-11.0.11+9" ]; then - echo "Skipping download of jdk release" -else - ARCHIVE="OpenJDK11U-jdk_x64_linux_hotspot_11.0.11_9.tar.gz" - wget "https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.11%2B9/$ARCHIVE" - tar -xf "$ARCHIVE" - rm "$ARCHIVE" - - jimage extract --dir exploded-modules jdk-11.0.11+9/lib/modules -fi - if [ -d "jdk" ]; then echo "Skipping download of jdk sources" else diff --git a/src/zip.cpp b/src/zip.cpp index ddb9944..79610b2 100644 --- a/src/zip.cpp +++ b/src/zip.cpp @@ -39,7 +39,7 @@ ZipArchive::ZipArchive(std::string path) : archive(), path(std::move(path)), ent } } -ZipEntry const *ZipArchive::entry_for_path(std::string &filepath) const { +ZipEntry const *ZipArchive::entry_for_path(std::string const &filepath) const { if (auto found = entries.find(filepath); found != entries.end()) { return &found->second; } else { diff --git a/src/zip.hpp b/src/zip.hpp index 0ce4ca1..4d87dcb 100644 --- a/src/zip.hpp +++ b/src/zip.hpp @@ -2,6 +2,7 @@ #define SCHOKOVM_ZIP_HPP #include +#include #include #include @@ -33,7 +34,7 @@ struct ZipArchive { explicit ZipArchive(std::string path); - ZipEntry const *entry_for_path(std::string &filepath) const; + ZipEntry const *entry_for_path(std::string const &filepath) const; void read(ZipEntry const &entry, std::vector &buffer) const; };