summaryrefslogtreecommitdiff
path: root/libs/samba/src/main/cpp/jni.cpp
blob: 30f03a9b05e3a89b29be2e95b815ede98fa92b4a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include "jni.hpp"

#include <android/log.h>

namespace {

JavaVM *g_vm;

const char *_jni_error(jint err) {
  switch (err) {
    case JNI_OK:
      return "OK";
    case JNI_ERR:
      return "Unknown error";
    case JNI_EDETACHED:
      return "Thread detached from the VM";
    case JNI_EVERSION:
      return "JNI version error";
    case JNI_ENOMEM:
      return "Not enough memory";
    case JNI_EEXIST:
      return "VM already created";
    case JNI_EINVAL:
      return "Invalid arguments";
    default:
      return "Unexpected error";
  }
}

}  // namespace

namespace jni {

namespace internal {

void _abort_if_not_ok(const char *file, int line, jint ret) {
  if (ret == JNI_OK) [[likely]] return;
  __android_log_assert(nullptr, "jni", "JNI error: %s", _jni_error(ret));
}

void _abort_with_exception(const char* file, int line, JNIEnv* env) {
  auto throwable = jni::ExceptionOccurred(env);
  env->ExceptionClear();
  if (throwable) {
    auto throwable_class = jni::FindClass(env, "java/lang/Throwable");
    if (throwable_class) {
      auto throwable_toString = env->GetMethodID(throwable_class.get(),
                                                 "toString",
                                                 "()Ljava/lang/String;");
      if (throwable_toString) {
        auto description = jni::CallObjectMethod<jstring>(env, throwable,
                                                          throwable_toString);
        auto str = jni::StringToUTF8(env, description);
        __android_log_assert(nullptr, "jni", "JNI error: %s", str.c_str());
      }
    }
    env->ExceptionClear();
    __android_log_assert(nullptr, "jni",
                         "Unexpected NULL but no exception");
  }
}

}  // namespace internal

JNIEnv* AttachCurrentThread() {
  JNIEnv* env;
  auto ret = g_vm->AttachCurrentThread(&env, nullptr);
  ABORT_IF_NOT_OK(ret);
  return env;
}

JNIEnv* OnLoad(JavaVM* vm) {
  void* v_env;
  auto ret = vm->GetEnv(&v_env, JNI_VERSION);
  ABORT_IF_NOT_OK(ret);
  return reinterpret_cast<JNIEnv*>(v_env);
}

LocalRef<jclass> FindClass(JNIEnv *env, const char *name) {
  return {env, env->FindClass(name)};
}

LocalRef<jthrowable> ExceptionOccurred(JNIEnv* env) {
  return {env, env->ExceptionOccurred()};
}

std::string StringToUTF8(JNIEnv* env, const Ref<jstring>& str) {
  if (!str) return "null";
  auto len = env->GetStringUTFLength(str.get());
  std::string ret(len, ' ');
  env->GetStringUTFRegion(str.get(), 0, len, ret.data());
  // This returns modified UTF-8 encoding, don't care.
  return ret;
}

LocalRef<jstring> UTF8ToString(JNIEnv* env, const std::string& str) {
  return {env, env->NewStringUTF(str.c_str())};
}

LocalRef<jobjectArray> CreateArray(JNIEnv* env, const Ref<jclass>& element_class, std::vector<LocalRef<jobject>> objects) {
  auto ret = LocalRef<jobjectArray>(env, env->NewObjectArray(static_cast<jsize>(objects.size()), element_class.get(), nullptr));
  ABORT_IF_NULL(env, ret);
  jsize i = 0;
  for (auto&& obj : objects) {
    env->SetObjectArrayElement(ret.get(), i++, obj.release());
  }
  return ret;
}

}  // namespace jni