Created
July 24, 2018 16:51
-
-
Save notfoundry/0ec1308a4beea699e6a51e211453a68c to your computer and use it in GitHub Desktop.
JNI-querying IClassPath implementation for the JNIF C++ library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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