aboutsummaryrefslogtreecommitdiff
path: root/header_only_include/nativehelper/utils.h
blob: 30f239e56277dc3c78cdd37285aac46076596850 (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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * JNI utils for external use.
 *
 * This file may only be included by C++ code.
 */

#pragma once

#include <jni.h>

#include <string>

#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_utf_chars.h"

namespace android {
namespace jnihelp {

// Implementation details. DO NOT use directly.
namespace internal {

[[maybe_unused]] static const char* get_c_str(const char* str) { return str; }
[[maybe_unused]] static const char* get_c_str(const std::string& str) { return str.c_str(); }

}  // namespace internal

// A class that implicitly casts to the default values of various JNI types.
// Used for returning from a JNI method when an exception occurs, where we don't care about the
// return value.
class JniDefaultValue {
   public:
    operator jboolean() const { return JNI_FALSE; }
    operator jbyte() const { return 0; }
    operator jchar() const { return 0; }
    operator jshort() const { return 0; }
    operator jint() const { return 0; }
    operator jlong() const { return 0; }
    operator jfloat() const { return 0; }
    operator jdouble() const { return 0; }
    operator jobject() const { return nullptr; }
    operator jclass() const { return nullptr; }
    operator jstring() const { return nullptr; }
    operator jarray() const { return nullptr; }
    operator jobjectArray() const { return nullptr; }
    operator jbooleanArray() const { return nullptr; }
    operator jbyteArray() const { return nullptr; }
    operator jcharArray() const { return nullptr; }
    operator jshortArray() const { return nullptr; }
    operator jintArray() const { return nullptr; }
    operator jlongArray() const { return nullptr; }
    operator jfloatArray() const { return nullptr; }
    operator jdoubleArray() const { return nullptr; }
    operator jthrowable() const { return nullptr; }
};

// Gets `ScopedUtfChars` from a `jstring` expression.
//
// Throws `NullPointerException` and returns the default value if the given `jstring` is a null
// pointer.
//
// Examples:
//
// - If the function returns a value:
//
// jobject MyJniMethod(JNIEnv* env, jstring j_str) {
//   ScopedUtfChars str = GET_UTF_OR_RETURN(env, j_str);
//   // Safely use `str` here...
// }
//
// - If the function returns void:
//
// void MyJniMethod(JNIEnv* env, jstring j_str) {
//   ScopedUtfChars str = GET_UTF_OR_RETURN_VOID(env, j_str);
//   // Safely use `str` here...
// }
//
// The idiomatic way to construct an `std::string` using this macro (an additional string copy is
// performed):
//
// jobject MyJniMethod(JNIEnv* env, jstring j_str) {
//   std::string str(GET_UTF_OR_RETURN(env, j_str));
//   // Safely use `str` here...
// }
#define GET_UTF_OR_RETURN(env, expr) \
    GET_UTF_OR_RETURN_IMPL_((env), (expr), android::jnihelp::JniDefaultValue())
#define GET_UTF_OR_RETURN_VOID(env, expr) GET_UTF_OR_RETURN_IMPL_((env), (expr))

#define GET_UTF_OR_RETURN_IMPL_(env, expr, ...)                          \
    ({                                                                   \
        ScopedUtfChars tmp_scoped_utf_chars_(env, expr);                 \
        if (tmp_scoped_utf_chars_.c_str() == nullptr) {                  \
            /* Return with a pending exception from `ScopedUtfChars`. */ \
            return __VA_ARGS__;                                          \
        }                                                                \
        std::move(tmp_scoped_utf_chars_);                                \
    })

// Creates `ScopedLocalRef<jstring>` from a `const char*` or `std::string` expression using
// NewStringUTF.
//
// Throws `OutOfMemoryError` and returns the default value if the system runs out of memory.
//
// Examples:
//
// - If the function returns a value:
//
// jobject MyJniMethod(JNIEnv* env) {
//   std::string str = "foo";
//   ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN(env, str);
//   // Safely use `j_str` here...
// }
//
// - If the function returns void:
//
// void MyJniMethod(JNIEnv* env) {
//   std::string str = "foo";
//   ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN_VOID(env, str);
//   // Safely use `j_str` here...
// }
#define CREATE_UTF_OR_RETURN(env, expr) \
    CREATE_UTF_OR_RETURN_IMPL_((env), (expr), android::jnihelp::JniDefaultValue())
#define CREATE_UTF_OR_RETURN_VOID(env, expr) CREATE_UTF_OR_RETURN_IMPL_((env), (expr))

#define CREATE_UTF_OR_RETURN_IMPL_(env, expr, ...)                                             \
    ({                                                                                         \
        const char* tmp_c_str_ = android::jnihelp::internal::get_c_str(expr);                  \
        ScopedLocalRef<jstring> tmp_local_ref_(env, env->NewStringUTF(tmp_c_str_));            \
        /* `NewStringUTF` returns nullptr when OOM or the input is nullptr, but only throws an \
         * exception when OOM. */                                                              \
        if (tmp_local_ref_ == nullptr && tmp_c_str_ != nullptr) {                              \
            /* Return with a pending exception from `NewStringUTF`. */                         \
            return __VA_ARGS__;                                                                \
        }                                                                                      \
        std::move(tmp_local_ref_);                                                             \
    })

}  // namespace jnihelp
}  // namespace android