Skip to content

Instantly share code, notes, and snippets.

@notfoundry
Created July 24, 2018 16:51
Show Gist options
  • Select an option

  • Save notfoundry/0ec1308a4beea699e6a51e211453a68c to your computer and use it in GitHub Desktop.

Select an option

Save notfoundry/0ec1308a4beea699e6a51e211453a68c to your computer and use it in GitHub Desktop.
JNI-querying IClassPath implementation for the JNIF C++ library
#include <exception>
#include <string>
#include <memory>
#include <jni.h>
#include <jnif/jnif.hpp>
class jni_class_loader {
public:
explicit jni_class_loader (JNIEnv& env) : env(env) {
jclass loader_class = env.FindClass("java/lang/ClassLoader");
loader_get_resource_stream = env.GetMethodID(loader_class, "getResourceAsStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
env.DeleteLocalRef(loader_class);
jclass ostream_class = env.FindClass("java/io/ByteArrayOutputStream");
ostream_ctor = env.GetMethodID(ostream_class, "<init>", "()V");
ostream_write = env.GetMethodID(ostream_class, "write", "([BII)V");
ostream_to_byte_array = env.GetMethodID(ostream_class, "toByteArray", "()[B");
env.DeleteLocalRef(ostream_class);
jclass istream_class = env.FindClass("java/io/InputStream");
istream_read = env.GetMethodID(istream_class, "read", "([B)I");
env.DeleteLocalRef(istream_class);
}
std::unique_ptr<jnif::ClassFile> get_class(const std::string& class_name, jobject loader) {
std::string class_file_name = class_name + ".class";
jclass loader_class = env.FindClass("java/lang/ClassLoader");
jclass ostream_class = env.FindClass("java/io/ByteArrayOutputStream");
jclass istream_class = env.FindClass("java/io/InputStream");
//String target_name = class_file_name;
jstring target_name = env.NewStringUTF(class_file_name.c_str());
//InputStream istream = loader.getResourceAsStream(target_name);
jobject istream = env.CallObjectMethod(loader, loader_get_resource_stream, target_name);
if (istream == nullptr) {
env.DeleteLocalRef(istream);
env.DeleteLocalRef(target_name);
env.DeleteLocalRef(istream_class);
env.DeleteLocalRef(ostream_class);
env.DeleteLocalRef(loader_class);
//return null;
return std::unique_ptr<jnif::ClassFile>();
}
//ByteArrayOutputStream ostream = new ByteArrayOutputStream();
jobject ostream = env.NewObject(ostream_class, ostream_ctor);
//byte[] buf = new byte[0xFFFF];
jbyteArray buf = env.NewByteArray(0xFFFF);
//int bytes_read = istream.read(buf);
jint bytes_read = env.CallIntMethod(istream, istream_read, buf);
while (bytes_read != -1) {
//ostream.write(buf, 0, bytes_read);
env.CallVoidMethod(ostream, ostream_write, buf, jint(0), bytes_read);
//bytes_read = istream.read(buf);
bytes_read = env.CallIntMethod(istream, istream_read, buf);
}
//byte[] bytes_array = ostream.toByteArray();
auto bytes_array = reinterpret_cast<jbyteArray>(env.CallObjectMethod(ostream, ostream_to_byte_array));
jsize length = env.GetArrayLength(bytes_array);
jbyte* bytes = env.GetByteArrayElements(bytes_array, nullptr);
std::unique_ptr<jnif::ClassFile> class_file(new jnif::parser::ClassFileParser(reinterpret_cast<const jnif::u1*>(bytes), static_cast<jnif::u4>(length)));
env.ReleaseByteArrayElements(bytes_array, bytes, JNI_ABORT);
env.DeleteLocalRef(buf);
env.DeleteLocalRef(ostream);
env.DeleteLocalRef(istream);
env.DeleteLocalRef(target_name);
env.DeleteLocalRef(istream_class);
env.DeleteLocalRef(ostream_class);
env.DeleteLocalRef(loader_class);
return class_file;
}
private:
JNIEnv& env;
jmethodID loader_get_resource_stream;
jmethodID ostream_ctor;
jmethodID ostream_write;
jmethodID ostream_to_byte_array;
jmethodID istream_read;
};
class jni_classpath final : public jnif::IClassPath {
private:
constexpr static char const* object_class_name = "java/lang/Object";
constexpr static char const* nonexistent_class_name = "0";
struct class_not_loaded_exception : public std::exception {
explicit class_not_loaded_exception(std::string msg) : msg(std::move(msg)) {}
const char* what() const noexcept override {
return msg.c_str();
}
private:
std::string msg;
};
public:
jni_classpath(JNIEnv& jni, jobject loader, jnif::ClassHierarchy& class_hierarchy)
: jni(jni), loader(jni.NewGlobalRef(loader)), class_hierarchy(class_hierarchy), class_loader(jni) {}
~jni_classpath() override {
jni.DeleteGlobalRef(loader);
}
std::string getCommonSuperClass(const std::string& first_class, const std::string& second_class) override {
try {
load_class_and_add_to_hiearchy_if_not_loaded(first_class);
load_class_and_add_to_hiearchy_if_not_loaded(second_class);
std::string curr_super_class = first_class;
while (!is_assignable_from(second_class, curr_super_class)) {
load_class_and_add_to_hiearchy_if_not_loaded(curr_super_class);
curr_super_class = class_hierarchy.getSuperClass(curr_super_class);
if (!class_exists(curr_super_class)) return object_class_name;
}
return curr_super_class;
} catch (const class_not_loaded_exception& e) {
return object_class_name;
}
}
private:
bool class_exists(const std::string& class_name) const {
return class_name != nonexistent_class_name;
}
bool is_assignable_from(std::string sub_class, const std::string& super_class) {
while (class_exists(sub_class)) {
if (sub_class == super_class) return true;
load_class_and_add_to_hiearchy_if_not_loaded(sub_class);
sub_class = class_hierarchy.getSuperClass(sub_class);
}
return false;
}
void load_class_and_add_to_hiearchy_if_not_loaded(const std::string& class_name) {
if (!class_hierarchy.isDefined(class_name)) {
std::unique_ptr<jnif::ClassFile> cf = class_loader.get_class(class_name, loader);
if (cf) {
class_hierarchy.addClass(*cf);
} else {
throw class_not_loaded_exception(class_name);
}
}
}
JNIEnv& jni;
jobject loader;
jnif::ClassHierarchy& class_hierarchy;
jni_class_loader class_loader;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment