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
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* This is a modified version of Android's ScopedLocalRef class that can be
* found in the Android's JNI code.
*/
#pragma once
#include <jni.h>
#include <cstddef>
#include <type_traits>
namespace facebook::yoga::vanillajni {
/**
* ScopedLocalRef is a sort of smart reference that allows us to control the
* lifespan of a JNI local reference.
*
* This class is designed so that when a ScopedLocalRef goes out of scope, its
* destructor will delete -JNIEnv->DeleteLocalRef()- the underlying JNI
* reference.
*
* This class should be used to wrap all the local references that JNI
* gives us other than those that are passed to native methods at
* invocation time. The idea behind this is that in JNI we should be very
* explicit about the lifespan of local references. Local references can quickly
* get out of control, and the developer should always be very aware of the
* lifespan of each local reference that is created in JNI so that leaks are
* prevented.
*
* This class is very explicit in its behavior, and it does not allow to perform
* unexpected conversions or unexpected ownership transfer. In practice, this
* class acts as a unique pointer where the underlying JNI reference can have
* one and just one owner. Transferring ownership is allowed but it is an
* explicit operation (implemented via move semantics and also via explicitly
* API calls).
*
* As with standard JNI local references it is not a valid operation to keep a
* reference around between different native method calls.
*/
template <typename T>
class ScopedLocalRef {
static_assert(
std::is_same<T, jclass>() || std::is_same<T, jobject>() ||
std::is_same<T, jstring>() || std::is_same<T, jthrowable>() ||
std::is_same<T, jbyteArray>() || std::is_same<T, jintArray>() ||
std::is_same<T, jshortArray>() || std::is_same<T, jcharArray>() ||
std::is_same<T, jlongArray>() || std::is_same<T, jfloatArray>() ||
std::is_same<T, jdoubleArray>() || std::is_same<T, jobjectArray>() ||
std::is_same<T, jbooleanArray>(),
"ScopedLocalRef instantiated for invalid type");
public:
/**
* Constructs a ScopedLocalRef with a JNI local reference.
*
* @param localRef the local reference to wrap. Can be NULL.
*/
ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {}
/**
* Equivalent to ScopedLocalRef(env, NULL)
*/
explicit ScopedLocalRef(JNIEnv* env) : mEnv(env), mLocalRef(NULL) {}
/**
* Move construction is allowed.
*/
ScopedLocalRef(ScopedLocalRef&& s) noexcept
: mEnv(s.mEnv), mLocalRef(s.release()) {}
/**
* Move assignment is allowed.
*/
ScopedLocalRef& operator=(ScopedLocalRef&& s) noexcept {
reset(s.release());
mEnv = s.mEnv;
return *this;
}
~ScopedLocalRef() {
reset();
}
/**
* Deletes the currently held reference and reassigns a new one to the
* ScopedLocalRef.
*/
void reset(T ptr = NULL) {
if (ptr != mLocalRef) {
if (mLocalRef != NULL) {
mEnv->DeleteLocalRef(mLocalRef);
}
mLocalRef = ptr;
}
}
/**
* Makes this ScopedLocalRef not own the underlying JNI local reference. After
* calling this method, the ScopedLocalRef will not delete the JNI local
* reference when the ScopedLocalRef goes out of scope.
*/
T release() {
T localRef = mLocalRef;
mLocalRef = NULL;
return localRef;
}
/**
* Returns the underlying JNI local reference.
*/
T get() const {
return mLocalRef;
}
/**
* Returns true if the underlying JNI reference is not NULL.
*/
operator bool() const {
return mLocalRef != NULL;
}
ScopedLocalRef(const ScopedLocalRef& ref) = delete;
ScopedLocalRef& operator=(const ScopedLocalRef& other) = delete;
private:
JNIEnv* mEnv;
T mLocalRef;
};
template <typename T>
ScopedLocalRef<T> make_local_ref(JNIEnv* env, T localRef) {
return ScopedLocalRef<T>(env, localRef);
}
} // namespace facebook::yoga::vanillajni