#include #include #include #define LOG_TAG "QAuxv" #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__) #define PLOGE(fmt, args...) \ LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) namespace { jfieldID token_field; jfieldID class_loader_field; jmethodID load_class_method; // jmethodID get_declared_method; // jmethodID get_declared_field; jmethodID get_name_method; jmethodID get_declaring_class_method; jmethodID get_parameters_method; jmethodID get_class_name_method; jfieldID path_list_field; jfieldID element_field; jfieldID dex_file_field; jfieldID cookie_field; jclass LoadClass(JNIEnv *env, jobject class_loader, std::string_view descriptor) { if (descriptor.empty()) return nullptr; if (descriptor[0] != 'L') return nullptr; std::string name(descriptor.substr(1, descriptor.length() - 2)); std::replace(name.begin(), name.end(), '/', '.'); auto java_name = env->NewStringUTF(name.data()); auto res = (jclass)env->CallObjectMethod(class_loader, load_class_method, java_name, false); return res; } std::string GetClassDescriptor(JNIEnv *env, jclass clazz) { auto java_name = (jstring)env->CallObjectMethod(clazz, get_class_name_method); auto name = env->GetStringUTFChars(java_name, nullptr); std::string descriptor = name; std::replace(descriptor.begin(), descriptor.end(), '.', '/'); if (descriptor[0] != '[') { if (descriptor == "boolean") { descriptor = "Z"; } else if (descriptor == "byte") { descriptor = "B"; } else if (descriptor == "short") { descriptor = "S"; } else if (descriptor == "char") { descriptor = "C"; } else if (descriptor == "int") { descriptor = "I"; } else if (descriptor == "long") { descriptor = "J"; } else if (descriptor == "float") { descriptor = "F"; } else if (descriptor == "double") { descriptor = "D"; } descriptor = "L" + std::string(name) + ";"; } env->ReleaseStringUTFChars(java_name, name); return descriptor; } } // namespace struct MyDexFile { const void *begin_{}; size_t size_{}; const uint8_t *const data_begin_{}; const size_t data_size_{}; virtual ~MyDexFile() = default; }; extern "C" JNIEXPORT jlongArray JNICALL Java_me_iacn_biliroaming_utils_DexHelper_findMethodUsingString( JNIEnv *env, jobject thiz, jstring str, jboolean match_prefix, jlong return_type, jshort parameter_count, jstring parameter_shorty, jlong declaring_class, jlongArray parameter_types, jlongArray contains_parameter_types, jintArray dex_priority, jboolean find_first) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return nullptr; if (!str) return nullptr; auto str_ = env->GetStringUTFChars(str, nullptr); auto parameter_shorty_ = parameter_shorty ? env->GetStringUTFChars(parameter_shorty, nullptr) : nullptr; std::vector dex_priority_; jint *dex_priority_elements = nullptr; if (dex_priority) { dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); dex_priority_.assign(dex_priority_elements, dex_priority_elements + env->GetArrayLength(dex_priority)); } std::vector parameter_types_; jlong *parameter_types_elements = nullptr; if (parameter_types) { parameter_types_elements = env->GetLongArrayElements(parameter_types, nullptr); parameter_types_.assign(parameter_types_elements, parameter_types_elements + env->GetArrayLength(parameter_types)); } std::vector contains_parameter_types_; jlong *contains_parameter_types_elements = nullptr; if (contains_parameter_types) { contains_parameter_types_elements = env->GetLongArrayElements(contains_parameter_types, nullptr); contains_parameter_types_.assign( contains_parameter_types_elements, contains_parameter_types_elements + env->GetArrayLength(contains_parameter_types)); } auto out = helper->FindMethodUsingString( str_, match_prefix, return_type, parameter_count, parameter_shorty_ ? parameter_shorty_ : "", declaring_class, contains_parameter_types_, contains_parameter_types_, dex_priority_, find_first); env->ReleaseStringUTFChars(str, str_); if (parameter_shorty_) env->ReleaseStringUTFChars(parameter_shorty, parameter_shorty_); if (dex_priority_elements) env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, JNI_ABORT); if (parameter_types_elements) env->ReleaseLongArrayElements(parameter_types, parameter_types_elements, JNI_ABORT); if (contains_parameter_types_elements) env->ReleaseLongArrayElements(contains_parameter_types, contains_parameter_types_elements, JNI_ABORT); auto res = env->NewLongArray(static_cast(out.size())); auto res_element = env->GetLongArrayElements(res, nullptr); for (size_t i = 0; i < out.size(); ++i) { res_element[i] = out[i]; } env->ReleaseLongArrayElements(res, res_element, 0); return res; } extern "C" JNIEXPORT jlong JNICALL Java_me_iacn_biliroaming_utils_DexHelper_load(JNIEnv *env, jobject thiz, jobject class_loader) { if (!class_loader) return 0; auto path_list = env->GetObjectField(class_loader, path_list_field); if (!path_list) return 0; auto elements = (jobjectArray)env->GetObjectField(path_list, element_field); if (!elements) return 0; std::vector> images; for (auto i = 0, len = env->GetArrayLength(elements); i < len; ++i) { auto element = env->GetObjectArrayElement(elements, i); if (!element) continue; auto java_dex_file = env->GetObjectField(element, dex_file_field); if (!java_dex_file) continue; auto cookie = (jlongArray)env->GetObjectField(java_dex_file, cookie_field); if (!cookie) continue; auto dex_file_length = env->GetArrayLength(cookie); const auto *dex_files = reinterpret_cast( env->GetLongArrayElements(cookie, nullptr)); while (dex_file_length-- > 1) { const auto *dex_file = dex_files[dex_file_length]; if (!dex_file) { LOGE("dex_file is nullptr at %d", dex_file_length); continue; } LOGD("Got dex file %d", dex_file_length); if (dex::Reader::IsCompact(dex_file->begin_)) { images.emplace_back(dex_file->begin_, dex_file->size_, dex_file->data_begin_, dex_file->data_size_); } else { images.emplace_back(dex_file->begin_, dex_file->size_, nullptr, 0); } } } if (images.empty()) return 0; auto res = reinterpret_cast(new DexHelper(images)); env->SetLongField(thiz, token_field, res); return res; } extern "C" JNIEXPORT jlongArray JNICALL Java_me_iacn_biliroaming_utils_DexHelper_findMethodInvoking( JNIEnv *env, jobject thiz, jlong method_index, jlong return_type, jshort parameter_count, jstring parameter_shorty, jlong declaring_class, jlongArray parameter_types, jlongArray contains_parameter_types, jintArray dex_priority, jboolean find_first) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return nullptr; auto parameter_shorty_ = parameter_shorty ? env->GetStringUTFChars(parameter_shorty, nullptr) : nullptr; std::vector dex_priority_; jint *dex_priority_elements = nullptr; if (dex_priority) { dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); dex_priority_.assign(dex_priority_elements, dex_priority_elements + env->GetArrayLength(dex_priority)); } std::vector parameter_types_; jlong *parameter_types_elements = nullptr; if (parameter_types) { parameter_types_elements = env->GetLongArrayElements(parameter_types, nullptr); parameter_types_.assign(parameter_types_elements, parameter_types_elements + env->GetArrayLength(parameter_types)); } std::vector contains_parameter_types_; jlong *contains_parameter_types_elements = nullptr; if (contains_parameter_types) { contains_parameter_types_elements = env->GetLongArrayElements(contains_parameter_types, nullptr); contains_parameter_types_.assign( contains_parameter_types_elements, contains_parameter_types_elements + env->GetArrayLength(contains_parameter_types)); } auto out = helper->FindMethodInvoking( method_index, return_type, parameter_count, parameter_shorty_ ? parameter_shorty_ : "", declaring_class, contains_parameter_types_, contains_parameter_types_, dex_priority_, find_first); if (parameter_shorty_) env->ReleaseStringUTFChars(parameter_shorty, parameter_shorty_); if (dex_priority_elements) env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, JNI_ABORT); if (parameter_types_elements) env->ReleaseLongArrayElements(parameter_types, parameter_types_elements, JNI_ABORT); if (contains_parameter_types_elements) env->ReleaseLongArrayElements(contains_parameter_types, contains_parameter_types_elements, JNI_ABORT); auto res = env->NewLongArray(static_cast(out.size())); auto res_element = env->GetLongArrayElements(res, nullptr); for (size_t i = 0; i < out.size(); ++i) { res_element[i] = out[i]; } env->ReleaseLongArrayElements(res, res_element, 0); return res; } extern "C" JNIEXPORT jlongArray JNICALL Java_me_iacn_biliroaming_utils_DexHelper_findMethodInvoked( JNIEnv *env, jobject thiz, jlong method_index, jlong return_type, jshort parameter_count, jstring parameter_shorty, jlong declaring_class, jlongArray parameter_types, jlongArray contains_parameter_types, jintArray dex_priority, jboolean find_first) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return nullptr; auto parameter_shorty_ = parameter_shorty ? env->GetStringUTFChars(parameter_shorty, nullptr) : nullptr; std::vector dex_priority_; jint *dex_priority_elements = nullptr; if (dex_priority) { dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); dex_priority_.assign(dex_priority_elements, dex_priority_elements + env->GetArrayLength(dex_priority)); } std::vector parameter_types_; jlong *parameter_types_elements = nullptr; if (parameter_types) { parameter_types_elements = env->GetLongArrayElements(parameter_types, nullptr); parameter_types_.assign(parameter_types_elements, parameter_types_elements + env->GetArrayLength(parameter_types)); } std::vector contains_parameter_types_; jlong *contains_parameter_types_elements = nullptr; if (contains_parameter_types) { contains_parameter_types_elements = env->GetLongArrayElements(contains_parameter_types, nullptr); contains_parameter_types_.assign( contains_parameter_types_elements, contains_parameter_types_elements + env->GetArrayLength(contains_parameter_types)); } auto out = helper->FindMethodInvoked( method_index, return_type, parameter_count, parameter_shorty_ ? parameter_shorty_ : "", declaring_class, contains_parameter_types_, contains_parameter_types_, dex_priority_, find_first); if (parameter_shorty_) env->ReleaseStringUTFChars(parameter_shorty, parameter_shorty_); if (dex_priority_elements) env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, JNI_ABORT); if (parameter_types_elements) env->ReleaseLongArrayElements(parameter_types, parameter_types_elements, JNI_ABORT); if (contains_parameter_types_elements) env->ReleaseLongArrayElements(contains_parameter_types, contains_parameter_types_elements, JNI_ABORT); auto res = env->NewLongArray(static_cast(out.size())); auto res_element = env->GetLongArrayElements(res, nullptr); for (size_t i = 0; i < out.size(); ++i) { res_element[i] = out[i]; } env->ReleaseLongArrayElements(res, res_element, 0); return res; } extern "C" JNIEXPORT jlongArray JNICALL Java_me_iacn_biliroaming_utils_DexHelper_findMethodSettingField( JNIEnv *env, jobject thiz, jlong field_index, jlong return_type, jshort parameter_count, jstring parameter_shorty, jlong declaring_class, jlongArray parameter_types, jlongArray contains_parameter_types, jintArray dex_priority, jboolean find_first) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return nullptr; auto parameter_shorty_ = parameter_shorty ? env->GetStringUTFChars(parameter_shorty, nullptr) : nullptr; std::vector dex_priority_; jint *dex_priority_elements = nullptr; if (dex_priority) { dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); dex_priority_.assign(dex_priority_elements, dex_priority_elements + env->GetArrayLength(dex_priority)); } std::vector parameter_types_; jlong *parameter_types_elements = nullptr; if (parameter_types) { parameter_types_elements = env->GetLongArrayElements(parameter_types, nullptr); parameter_types_.assign(parameter_types_elements, parameter_types_elements + env->GetArrayLength(parameter_types)); } std::vector contains_parameter_types_; jlong *contains_parameter_types_elements = nullptr; if (contains_parameter_types) { contains_parameter_types_elements = env->GetLongArrayElements(contains_parameter_types, nullptr); contains_parameter_types_.assign( contains_parameter_types_elements, contains_parameter_types_elements + env->GetArrayLength(contains_parameter_types)); } auto out = helper->FindMethodSettingField( field_index, return_type, parameter_count, parameter_shorty_ ? parameter_shorty_ : "", declaring_class, contains_parameter_types_, contains_parameter_types_, dex_priority_, find_first); if (parameter_shorty_) env->ReleaseStringUTFChars(parameter_shorty, parameter_shorty_); if (dex_priority_elements) env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, JNI_ABORT); if (parameter_types_elements) env->ReleaseLongArrayElements(parameter_types, parameter_types_elements, JNI_ABORT); if (contains_parameter_types_elements) env->ReleaseLongArrayElements(contains_parameter_types, contains_parameter_types_elements, JNI_ABORT); auto res = env->NewLongArray(static_cast(out.size())); auto res_element = env->GetLongArrayElements(res, nullptr); for (size_t i = 0; i < out.size(); ++i) { res_element[i] = out[i]; } env->ReleaseLongArrayElements(res, res_element, 0); return res; } extern "C" JNIEXPORT jlongArray JNICALL Java_me_iacn_biliroaming_utils_DexHelper_findMethodGettingField( JNIEnv *env, jobject thiz, jlong field_index, jlong return_type, jshort parameter_count, jstring parameter_shorty, jlong declaring_class, jlongArray parameter_types, jlongArray contains_parameter_types, jintArray dex_priority, jboolean find_first) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return nullptr; auto parameter_shorty_ = parameter_shorty ? env->GetStringUTFChars(parameter_shorty, nullptr) : nullptr; std::vector dex_priority_; jint *dex_priority_elements = nullptr; if (dex_priority) { dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); dex_priority_.assign(dex_priority_elements, dex_priority_elements + env->GetArrayLength(dex_priority)); } std::vector parameter_types_; jlong *parameter_types_elements = nullptr; if (parameter_types) { parameter_types_elements = env->GetLongArrayElements(parameter_types, nullptr); parameter_types_.assign(parameter_types_elements, parameter_types_elements + env->GetArrayLength(parameter_types)); } std::vector contains_parameter_types_; jlong *contains_parameter_types_elements = nullptr; if (contains_parameter_types) { contains_parameter_types_elements = env->GetLongArrayElements(contains_parameter_types, nullptr); contains_parameter_types_.assign( contains_parameter_types_elements, contains_parameter_types_elements + env->GetArrayLength(contains_parameter_types)); } auto out = helper->FindMethodGettingField( field_index, return_type, parameter_count, parameter_shorty_ ? parameter_shorty_ : "", declaring_class, contains_parameter_types_, contains_parameter_types_, dex_priority_, find_first); if (parameter_shorty_) env->ReleaseStringUTFChars(parameter_shorty, parameter_shorty_); if (dex_priority_elements) env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, JNI_ABORT); if (parameter_types_elements) env->ReleaseLongArrayElements(parameter_types, parameter_types_elements, JNI_ABORT); if (contains_parameter_types_elements) env->ReleaseLongArrayElements(contains_parameter_types, contains_parameter_types_elements, JNI_ABORT); auto res = env->NewLongArray(static_cast(out.size())); auto res_element = env->GetLongArrayElements(res, nullptr); for (size_t i = 0; i < out.size(); ++i) { res_element[i] = out[i]; } env->ReleaseLongArrayElements(res, res_element, 0); return res; } extern "C" JNIEXPORT jlongArray JNICALL Java_me_iacn_biliroaming_utils_DexHelper_findField(JNIEnv *env, jobject thiz, jlong type, jintArray dex_priority, jboolean find_first) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); std::vector dex_priority_; jint *dex_priority_elements = nullptr; if (dex_priority) { dex_priority_elements = env->GetIntArrayElements(dex_priority, nullptr); dex_priority_.assign(dex_priority_elements, dex_priority_elements + env->GetArrayLength(dex_priority)); } auto out = helper->FindField(type, dex_priority_, find_first); if (dex_priority_elements) env->ReleaseIntArrayElements(dex_priority, dex_priority_elements, JNI_ABORT); auto res = env->NewLongArray(static_cast(out.size())); auto res_element = env->GetLongArrayElements(res, nullptr); for (size_t i = 0; i < out.size(); ++i) { res_element[i] = out[i]; } env->ReleaseLongArrayElements(res, res_element, 0); return res; } extern "C" JNIEXPORT jobject JNICALL Java_me_iacn_biliroaming_utils_DexHelper_decodeMethodIndex(JNIEnv *env, jobject thiz, jlong method_index) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return nullptr; auto out = helper->DecodeMethod(method_index); auto cl = env->GetObjectField(thiz, class_loader_field); auto clazz = LoadClass(env, cl, out.declaring_class.name); env->DeleteLocalRef(cl); std::string sig; sig.reserve(4096); sig += "("; for (const auto ¶m : out.parameters) { sig += param.name; } sig += ")"; sig += out.return_type.name; auto *method = env->GetMethodID(clazz, out.name.data(), sig.data()); if ((*env).ExceptionCheck()) { (*env).ExceptionClear(); } if (!method) { method = env->GetStaticMethodID(clazz, out.name.data(), sig.data()); } return env->ToReflectedMethod(clazz, method, false); } extern "C" JNIEXPORT jobject JNICALL Java_me_iacn_biliroaming_utils_DexHelper_decodeFieldIndex(JNIEnv *env, jobject thiz, jlong field_index) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return nullptr; auto out = helper->DecodeField(field_index); auto cl = env->GetObjectField(thiz, class_loader_field); auto clazz = LoadClass(env, cl, out.declaring_class.name); env->DeleteLocalRef(cl); return env->ToReflectedField( clazz, env->GetFieldID(clazz, out.name.data(), out.type.name.data()), false); } extern "C" JNIEXPORT jlong JNICALL Java_me_iacn_biliroaming_utils_DexHelper_encodeClassIndex(JNIEnv *env, jobject thiz, jclass clazz) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); return helper->CreateClassIndex(GetClassDescriptor(env, clazz)); } extern "C" JNIEXPORT jlong JNICALL Java_me_iacn_biliroaming_utils_DexHelper_encodeFieldIndex(JNIEnv *env, jobject thiz, jobject field) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return -1; auto java_name = (jstring)env->CallObjectMethod(field, get_name_method); auto clazz = (jclass)env->CallObjectMethod(field, get_declaring_class_method); auto name = env->GetStringUTFChars(java_name, nullptr); auto res = helper->CreateFieldIndex(name, GetClassDescriptor(env, clazz)); env->ReleaseStringUTFChars(java_name, name); return res; } extern "C" JNIEXPORT jlong JNICALL Java_me_iacn_biliroaming_utils_DexHelper_encodeMethodIndex(JNIEnv *env, jobject thiz, jobject method) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return -1; auto java_name = (jstring)env->CallObjectMethod(method, get_name_method); auto clazz = (jclass)env->CallObjectMethod(method, get_declaring_class_method); auto name = env->GetStringUTFChars(java_name, nullptr); auto params = (jobjectArray)env->CallObjectMethod(method, get_parameters_method); auto param_len = env->GetArrayLength(params); std::vector param_descriptors; param_descriptors.reserve(param_len); for (size_t i = 0; i < param_len; ++i) { auto param = (jclass)env->GetObjectArrayElement(params, i); param_descriptors.emplace_back(GetClassDescriptor(env, param)); env->DeleteLocalRef(param); } std::vector params_name; params_name.reserve(param_descriptors.size()); for (const auto &descriptor : param_descriptors) { params_name.emplace_back(descriptor); } auto res = helper->CreateMethodIndex(name, GetClassDescriptor(env, clazz), params_name); env->ReleaseStringUTFChars(java_name, name); return res; } extern "C" JNIEXPORT jclass JNICALL Java_me_iacn_biliroaming_utils_DexHelper_decodeClassIndex(JNIEnv *env, jobject thiz, jlong class_index) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return nullptr; auto out = helper->DecodeClass(class_index); auto cl = env->GetObjectField(thiz, class_loader_field); auto res = LoadClass(env, cl, out.name); env->DeleteLocalRef(cl); return res; } extern "C" JNIEXPORT void JNICALL Java_me_iacn_biliroaming_utils_DexHelper_close(JNIEnv *env, jobject thiz) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); env->SetLongField(thiz, token_field, jlong(0)); delete helper; } extern "C" JNIEXPORT jint JNICALL BILI_JNI_OnLoad(JavaVM *vm, void *) { JNIEnv *env = nullptr; if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_4) != JNI_OK) { return -1; } jclass helper = env->FindClass("me/iacn/biliroaming/utils/DexHelper"); token_field = env->GetFieldID(helper, "token", "J"); class_loader_field = env->GetFieldID(helper, "classLoader", "Ljava/lang/ClassLoader;"); auto class_loader = env->FindClass("java/lang/ClassLoader"); load_class_method = env->GetMethodID( class_loader, "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;"); auto member = env->FindClass("java/lang/reflect/Member"); get_declaring_class_method = env->GetMethodID(member, "getDeclaringClass", "()Ljava/lang/Class;"); get_name_method = env->GetMethodID(member, "getName", "()Ljava/lang/String;"); auto executable = env->FindClass("java/lang/reflect/Executable"); get_parameters_method = env->GetMethodID(executable, "getParameterTypes", "()[Ljava/lang/Class;"); auto clazz = env->FindClass("java/lang/Class"); get_class_name_method = env->GetMethodID(clazz, "getName", "()Ljava/lang/String;"); auto dex_class_loader = env->FindClass("dalvik/system/BaseDexClassLoader"); path_list_field = env->GetFieldID(dex_class_loader, "pathList", "Ldalvik/system/DexPathList;"); auto dex_path_list = env->FindClass("dalvik/system/DexPathList"); element_field = env->GetFieldID(dex_path_list, "dexElements", "[Ldalvik/system/DexPathList$Element;"); auto element = env->FindClass("dalvik/system/DexPathList$Element"); dex_file_field = env->GetFieldID(element, "dexFile", "Ldalvik/system/DexFile;"); auto dex_file = env->FindClass("dalvik/system/DexFile"); cookie_field = env->GetFieldID(dex_file, "mCookie", "Ljava/lang/Object;"); return JNI_VERSION_1_4; } extern "C" JNIEXPORT void JNICALL Java_me_iacn_biliroaming_utils_DexHelper_createFullCache(JNIEnv *env, jobject thiz) { auto *helper = reinterpret_cast(env->GetLongField(thiz, token_field)); if (!helper) return; helper->CreateFullCache(); }