Skip to content

Commit

Permalink
BootstrapClassLoader + lazy class loading
Browse files Browse the repository at this point in the history
  • Loading branch information
RobertObkircher committed May 31, 2021
1 parent 5d02752 commit 31b7877
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 96 deletions.
1 change: 1 addition & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion jdk/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/exploded-modules/
/jdk/
/jdk-11.0.11+9/
/jdk/
2 changes: 2 additions & 0 deletions jdk/download.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 10 additions & 4 deletions src/classfile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CONSTANT_Class_info *> interfaces;
std::vector<field_info> fields;
std::vector<method_info> methods;
Expand All @@ -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;
}
}
Expand All @@ -819,6 +823,8 @@ struct ClassFile {
[[nodiscard]] inline bool is_interface() const {
return (access_flags & static_cast<u2>(ClassFileAccessFlags::ACC_INTERFACE)) != 0;
}

[[nodiscard]] inline std::string const &name() const { return this_class->name->value; }
};


Expand Down
96 changes: 83 additions & 13 deletions src/classloading.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,76 @@
#include <filesystem>
#include <utility>

#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<ClassFile> 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<ClassFile>(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<ClassFile>(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) {
Expand Down Expand Up @@ -52,7 +124,7 @@ static bool initialize_class(ClassFile *clazz, Thread &thread, Frame &frame) {
}


bool resolve_class(std::unordered_map<std::string_view, ClassFile *> &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;
Expand All @@ -62,12 +134,11 @@ bool resolve_class(std::unordered_map<std::string_view, ClassFile *> &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;
Expand All @@ -80,13 +151,12 @@ bool resolve_class(std::unordered_map<std::string_view, ClassFile *> &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
Expand All @@ -105,7 +175,7 @@ bool resolve_class(std::unordered_map<std::string_view, ClassFile *> &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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
25 changes: 24 additions & 1 deletion src/classloading.hpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,38 @@
#ifndef SCHOKOVM_CLASSLOADING_HPP
#define SCHOKOVM_CLASSLOADING_HPP

#include <optional>
#include <unordered_map>

#include "classfile.hpp"
#include "interpreter.hpp"
#include "zip.hpp"

struct ClassPathEntry {
std::string module_name;
std::unordered_map<std::string, std::unique_ptr<ClassFile>> 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<ClassPathEntry> class_path_entries;
std::vector<char> 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<std::string_view, ClassFile *> &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);
Expand Down
Loading

0 comments on commit 31b7877

Please sign in to comment.