1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <new>
20 #include "CoreClasses.h"
21
22 namespace facebook {
23 namespace jni {
24
25 template <typename T>
getPlainJniReference(T ref)26 inline enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref) {
27 return ref;
28 }
29
30 template <typename T>
getPlainJniReference(alias_ref<T> ref)31 inline JniType<T> getPlainJniReference(alias_ref<T> ref) {
32 return ref.get();
33 }
34
35 template <typename T, typename A>
getPlainJniReference(const base_owned_ref<T,A> & ref)36 inline JniType<T> getPlainJniReference(const base_owned_ref<T, A>& ref) {
37 return ref.get();
38 }
39
40 namespace detail {
41 template <typename Repr>
42 struct ReprAccess {
43 using javaobject = JniType<Repr>;
setReprAccess44 static void set(Repr& repr, javaobject obj) noexcept {
45 repr.JObjectBase::set(obj);
46 }
getReprAccess47 static javaobject get(const Repr& repr) {
48 return static_cast<javaobject>(repr.JObjectBase::get());
49 }
50 };
51
52 namespace {
53 template <typename Repr>
StaticAssertValidRepr()54 void StaticAssertValidRepr() noexcept {
55 static_assert(
56 std::is_base_of<JObject, Repr>::value,
57 "A smart ref representation must be derived from JObject.");
58 static_assert(
59 IsPlainJniReference<JniType<Repr>>(), "T must be a JNI reference");
60 static_assert(sizeof(Repr) == sizeof(JObjectBase), "");
61 static_assert(alignof(Repr) == alignof(JObjectBase), "");
62 }
63 } // namespace
64
65 template <typename Repr>
ReprStorage(JniType<Repr> obj)66 ReprStorage<Repr>::ReprStorage(JniType<Repr> obj) noexcept {
67 StaticAssertValidRepr<Repr>();
68 set(obj);
69 }
70
71 template <typename Repr>
set(JniType<Repr> obj)72 void ReprStorage<Repr>::set(JniType<Repr> obj) noexcept {
73 new (&storage_) Repr;
74 ReprAccess<Repr>::set(get(), obj);
75 }
76
77 template <typename Repr>
get()78 Repr& ReprStorage<Repr>::get() noexcept {
79 return *reinterpret_cast<Repr*>(&storage_);
80 }
81
82 template <typename Repr>
get()83 const Repr& ReprStorage<Repr>::get() const noexcept {
84 return *reinterpret_cast<const Repr*>(&storage_);
85 }
86
87 template <typename Repr>
jobj()88 JniType<Repr> ReprStorage<Repr>::jobj() const noexcept {
89 return ReprAccess<Repr>::get(get());
90 }
91
92 template <typename Repr>
swap(ReprStorage & other)93 void ReprStorage<Repr>::swap(ReprStorage& other) noexcept {
94 StaticAssertValidRepr<Repr>();
95 using std::swap;
96 swap(get(), other.get());
97 }
98
set(jobject reference)99 inline void JObjectBase::set(jobject reference) noexcept {
100 this_ = reference;
101 }
102
get()103 inline jobject JObjectBase::get() const noexcept {
104 return this_;
105 }
106
107 template <typename T, typename Alloc>
make_ref(const T & reference)108 enable_if_t<IsNonWeakReference<T>(), plain_jni_reference_t<T>> make_ref(
109 const T& reference) {
110 auto old_reference = getPlainJniReference(reference);
111 if (!old_reference) {
112 return nullptr;
113 }
114
115 auto ref = Alloc{}.newReference(old_reference);
116 if (!ref) {
117 // Note that we end up here if we pass a weak ref that refers to a collected
118 // object. Thus, it's hard to come up with a reason why this function should
119 // be used with weak references.
120 throw std::bad_alloc{};
121 }
122
123 return static_cast<plain_jni_reference_t<T>>(ref);
124 }
125
126 } // namespace detail
127
128 template <typename T>
adopt_local(T ref)129 inline local_ref<T> adopt_local(T ref) noexcept {
130 static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
131 return local_ref<T>{ref};
132 }
133
134 template <typename T>
adopt_global(T ref)135 inline global_ref<T> adopt_global(T ref) noexcept {
136 static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
137 return global_ref<T>{ref};
138 }
139
140 template <typename T>
adopt_weak_global(T ref)141 inline weak_ref<T> adopt_weak_global(T ref) noexcept {
142 static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
143 return weak_ref<T>{ref};
144 }
145
146 template <typename T>
wrap_alias(T ref)147 inline enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(
148 T ref) noexcept {
149 return alias_ref<T>(ref);
150 }
151
152 template <typename T>
153 enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
154
155 template <typename T>
156 enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
make_local(const T & ref)157 make_local(const T& ref) {
158 return adopt_local(detail::make_ref<T, LocalReferenceAllocator>(ref));
159 }
160
161 template <typename T>
162 enable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>
make_global(const T & ref)163 make_global(const T& ref) {
164 return adopt_global(detail::make_ref<T, GlobalReferenceAllocator>(ref));
165 }
166
167 template <typename T>
168 enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
make_weak(const T & ref)169 make_weak(const T& ref) {
170 return adopt_weak_global(
171 detail::make_ref<T, WeakGlobalReferenceAllocator>(ref));
172 }
173
174 template <typename T1, typename T2>
175 inline enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
176 operator==(const T1& a, const T2& b) {
177 return isSameObject(getPlainJniReference(a), getPlainJniReference(b));
178 }
179
180 template <typename T1, typename T2>
181 inline enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
182 operator!=(const T1& a, const T2& b) {
183 return !(a == b);
184 }
185
186 template <typename T1>
187 inline enable_if_t<IsNonWeakReference<T1>(), bool> operator==(
188 const T1& a,
189 std::nullptr_t) {
190 return getPlainJniReference(a) == nullptr;
191 }
192
193 template <typename T1>
194 inline enable_if_t<IsNonWeakReference<T1>(), bool> operator==(
195 std::nullptr_t,
196 const T1& a) {
197 return nullptr == getPlainJniReference(a);
198 }
199
200 template <typename T1>
201 inline enable_if_t<IsNonWeakReference<T1>(), bool> operator!=(
202 const T1& a,
203 std::nullptr_t) {
204 return !(a == nullptr);
205 }
206
207 template <typename T1>
208 inline enable_if_t<IsNonWeakReference<T1>(), bool> operator!=(
209 std::nullptr_t,
210 const T1& a) {
211 return !(nullptr == getPlainJniReference(a));
212 }
213
214 // base_owned_ref
215 // ///////////////////////////////////////////////////////////////////////
216
217 template <typename T, typename Alloc>
base_owned_ref()218 inline base_owned_ref<T, Alloc>::base_owned_ref() noexcept
219 : base_owned_ref(nullptr) {}
220
221 template <typename T, typename Alloc>
base_owned_ref(std::nullptr_t t)222 inline base_owned_ref<T, Alloc>::base_owned_ref(std::nullptr_t t) noexcept
223 : base_owned_ref(static_cast<javaobject>(nullptr)) {
224 (void)t;
225 }
226
227 template <typename T, typename Alloc>
base_owned_ref(const base_owned_ref & other)228 inline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref& other)
229 : storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))} {}
230
231 template <typename T, typename Alloc>
232 template <typename U>
base_owned_ref(const base_owned_ref<U,Alloc> & other)233 inline base_owned_ref<T, Alloc>::base_owned_ref(
234 const base_owned_ref<U, Alloc>& other)
235 : storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))} {
236 static_assert(std::is_convertible<JniType<U>, javaobject>::value, "");
237 }
238
239 template <typename T, typename Alloc>
base_owned_ref(javaobject reference)240 inline facebook::jni::base_owned_ref<T, Alloc>::base_owned_ref(
241 javaobject reference) noexcept
242 : storage_(reference) {
243 assert(Alloc{}.verifyReference(reference));
244 internal::dbglog("New wrapped ref=%p this=%p", get(), this);
245 }
246
247 template <typename T, typename Alloc>
base_owned_ref(base_owned_ref<T,Alloc> && other)248 inline base_owned_ref<T, Alloc>::base_owned_ref(
249 base_owned_ref<T, Alloc>&& other) noexcept
250 : storage_(other.get()) {
251 internal::dbglog("New move from ref=%p other=%p", other.get(), &other);
252 internal::dbglog("New move to ref=%p this=%p", get(), this);
253 // JObject is a simple type and does not support move semantics so we
254 // explicitly clear other
255 other.set(nullptr);
256 }
257
258 template <typename T, typename Alloc>
259 template <typename U>
base_owned_ref(base_owned_ref<U,Alloc> && other)260 base_owned_ref<T, Alloc>::base_owned_ref(
261 base_owned_ref<U, Alloc>&& other) noexcept
262 : storage_(other.get()) {
263 internal::dbglog("New move from ref=%p other=%p", other.get(), &other);
264 internal::dbglog("New move to ref=%p this=%p", get(), this);
265 // JObject is a simple type and does not support move semantics so we
266 // explicitly clear other
267 other.set(nullptr);
268 }
269
270 template <typename T, typename Alloc>
~base_owned_ref()271 inline base_owned_ref<T, Alloc>::~base_owned_ref() noexcept {
272 reset();
273 internal::dbglog("Ref destruct ref=%p this=%p", get(), this);
274 }
275
276 template <typename T, typename Alloc>
277 inline auto base_owned_ref<T, Alloc>::release() noexcept -> javaobject {
278 auto value = get();
279 internal::dbglog("Ref release ref=%p this=%p", value, this);
280 set(nullptr);
281 return value;
282 }
283
284 template <typename T, typename Alloc>
reset()285 inline void base_owned_ref<T, Alloc>::reset() noexcept {
286 reset(nullptr);
287 }
288
289 template <typename T, typename Alloc>
reset(javaobject reference)290 inline void base_owned_ref<T, Alloc>::reset(javaobject reference) noexcept {
291 if (get()) {
292 assert(Alloc{}.verifyReference(reference));
293 Alloc{}.deleteReference(get());
294 }
295 set(reference);
296 }
297
298 template <typename T, typename Alloc>
299 inline auto base_owned_ref<T, Alloc>::get() const noexcept -> javaobject {
300 return storage_.jobj();
301 }
302
303 template <typename T, typename Alloc>
set(javaobject ref)304 inline void base_owned_ref<T, Alloc>::set(javaobject ref) noexcept {
305 storage_.set(ref);
306 }
307
308 // weak_ref
309 // ///////////////////////////////////////////////////////////////////////
310
311 template <typename T>
312 inline weak_ref<T>& weak_ref<T>::operator=(const weak_ref& other) {
313 auto otherCopy = other;
314 swap(*this, otherCopy);
315 return *this;
316 }
317
318 template <typename T>
319 inline weak_ref<T>& weak_ref<T>::operator=(weak_ref<T>&& rhs) noexcept {
320 internal::dbglog(
321 "Op= move ref=%p this=%p oref=%p other=%p", get(), this, rhs.get(), &rhs);
322 reset(rhs.release());
323 return *this;
324 }
325
326 template <typename T>
lockLocal()327 local_ref<T> weak_ref<T>::lockLocal() const {
328 return adopt_local(
329 static_cast<javaobject>(LocalReferenceAllocator{}.newReference(get())));
330 }
331
332 template <typename T>
lockGlobal()333 global_ref<T> weak_ref<T>::lockGlobal() const {
334 return adopt_global(
335 static_cast<javaobject>(GlobalReferenceAllocator{}.newReference(get())));
336 }
337
338 template <typename T>
swap(weak_ref<T> & a,weak_ref<T> & b)339 inline void swap(weak_ref<T>& a, weak_ref<T>& b) noexcept {
340 internal::dbglog(
341 "Ref swap a.ref=%p a=%p b.ref=%p b=%p", a.get(), &a, b.get(), &b);
342 a.storage_.swap(b.storage_);
343 }
344
345 // basic_strong_ref
346 // ////////////////////////////////////////////////////////////////////////////
347
348 template <typename T, typename Alloc>
349 inline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(
350 const basic_strong_ref& other) {
351 auto otherCopy = other;
352 swap(*this, otherCopy);
353 return *this;
354 }
355
356 template <typename T, typename Alloc>
357 inline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(
358 basic_strong_ref<T, Alloc>&& rhs) noexcept {
359 internal::dbglog(
360 "Op= move ref=%p this=%p oref=%p other=%p", get(), this, rhs.get(), &rhs);
361 reset(rhs.release());
362 return *this;
363 }
364
365 template <typename T, typename Alloc>
releaseAlias()366 inline alias_ref<T> basic_strong_ref<T, Alloc>::releaseAlias() noexcept {
367 return wrap_alias(release());
368 }
369
370 template <typename T, typename Alloc>
371 inline basic_strong_ref<T, Alloc>::operator bool() const noexcept {
372 return get() != nullptr;
373 }
374
375 template <typename T, typename Alloc>
376 inline auto basic_strong_ref<T, Alloc>::operator->() noexcept -> Repr* {
377 return &storage_.get();
378 }
379
380 template <typename T, typename Alloc>
381 inline auto basic_strong_ref<T, Alloc>::operator->() const noexcept
382 -> const Repr* {
383 return &storage_.get();
384 }
385
386 template <typename T, typename Alloc>
387 inline auto basic_strong_ref<T, Alloc>::operator*() noexcept -> Repr& {
388 return storage_.get();
389 }
390
391 template <typename T, typename Alloc>
392 inline auto basic_strong_ref<T, Alloc>::operator*() const noexcept
393 -> const Repr& {
394 return storage_.get();
395 }
396
397 template <typename T, typename Alloc>
swap(basic_strong_ref<T,Alloc> & a,basic_strong_ref<T,Alloc> & b)398 inline void swap(
399 basic_strong_ref<T, Alloc>& a,
400 basic_strong_ref<T, Alloc>& b) noexcept {
401 internal::dbglog(
402 "Ref swap a.ref=%p a=%p b.ref=%p b=%p", a.get(), &a, b.get(), &b);
403 using std::swap;
404 a.storage_.swap(b.storage_);
405 }
406
407 // alias_ref
408 // //////////////////////////////////////////////////////////////////////////////
409
410 template <typename T>
alias_ref()411 inline alias_ref<T>::alias_ref() noexcept : storage_{nullptr} {}
412
413 template <typename T>
alias_ref(std::nullptr_t)414 inline alias_ref<T>::alias_ref(std::nullptr_t) noexcept : storage_{nullptr} {}
415
416 template <typename T>
alias_ref(const alias_ref & other)417 inline alias_ref<T>::alias_ref(const alias_ref& other) noexcept
418 : storage_{other.get()} {}
419
420 template <typename T>
alias_ref(javaobject ref)421 inline alias_ref<T>::alias_ref(javaobject ref) noexcept : storage_(ref) {
422 assert(
423 LocalReferenceAllocator{}.verifyReference(ref) ||
424 GlobalReferenceAllocator{}.verifyReference(ref));
425 }
426
427 template <typename T>
428 template <typename TOther, typename /* for SFINAE */>
alias_ref(alias_ref<TOther> other)429 inline alias_ref<T>::alias_ref(alias_ref<TOther> other) noexcept
430 : storage_{other.get()} {}
431
432 template <typename T>
433 template <typename TOther, typename AOther, typename /* for SFINAE */>
alias_ref(const basic_strong_ref<TOther,AOther> & other)434 inline alias_ref<T>::alias_ref(
435 const basic_strong_ref<TOther, AOther>& other) noexcept
436 : storage_{other.get()} {}
437
438 template <typename T>
439 inline alias_ref<T>& alias_ref<T>::operator=(alias_ref other) noexcept {
440 swap(*this, other);
441 return *this;
442 }
443
444 template <typename T>
445 inline alias_ref<T>::operator bool() const noexcept {
446 return get() != nullptr;
447 }
448
449 template <typename T>
450 inline auto facebook::jni::alias_ref<T>::get() const noexcept -> javaobject {
451 return storage_.jobj();
452 }
453
454 template <typename T>
455 inline auto alias_ref<T>::operator->() noexcept -> Repr* {
456 return &(**this);
457 }
458
459 template <typename T>
460 inline auto alias_ref<T>::operator->() const noexcept -> const Repr* {
461 return &(**this);
462 }
463
464 template <typename T>
465 inline auto alias_ref<T>::operator*() noexcept -> Repr& {
466 return storage_.get();
467 }
468
469 template <typename T>
470 inline auto alias_ref<T>::operator*() const noexcept -> const Repr& {
471 return storage_.get();
472 }
473
474 template <typename T>
set(javaobject ref)475 inline void alias_ref<T>::set(javaobject ref) noexcept {
476 storage_.set(ref);
477 }
478
479 template <typename T>
swap(alias_ref<T> & a,alias_ref<T> & b)480 inline void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept {
481 a.storage_.swap(b.storage_);
482 }
483
484 // Could reduce code duplication by using a pointer-to-function
485 // template argument. I'm not sure whether that would make the code
486 // more maintainable (DRY), or less (too clever/confusing.).
487 template <typename T, typename U>
static_ref_cast(const local_ref<U> & ref)488 enable_if_t<IsPlainJniReference<JniType<T>>(), local_ref<T>> static_ref_cast(
489 const local_ref<U>& ref) noexcept {
490 JniType<T> p = static_cast<JniType<T>>(ref.get());
491 return make_local(p);
492 }
493
494 template <typename T, typename U>
static_ref_cast(const global_ref<U> & ref)495 enable_if_t<IsPlainJniReference<JniType<T>>(), global_ref<T>> static_ref_cast(
496 const global_ref<U>& ref) noexcept {
497 JniType<T> p = static_cast<JniType<T>>(ref.get());
498 return make_global(p);
499 }
500
501 template <typename T, typename U>
static_ref_cast(const alias_ref<U> & ref)502 enable_if_t<IsPlainJniReference<JniType<T>>(), alias_ref<T>> static_ref_cast(
503 const alias_ref<U>& ref) noexcept {
504 JniType<T> p = static_cast<JniType<T>>(ref.get());
505 return wrap_alias(p);
506 }
507
508 template <typename T, typename RefType>
509 auto dynamic_ref_cast(const RefType& ref)
510 -> enable_if_t<
511 IsPlainJniReference<JniType<T>>(),
512 decltype(static_ref_cast<T>(ref))> {
513 if (!ref) {
514 return decltype(static_ref_cast<T>(ref))();
515 }
516
517 static alias_ref<jclass> target_class =
518 findClassStatic(jtype_traits<T>::kBaseName.c_str());
519 if (!target_class) {
520 throwNewJavaException(
521 "java/lang/ClassCastException",
522 "Could not find class %s.",
523 jtype_traits<T>::kBaseName.c_str());
524 }
525
526 local_ref<jclass> source_class = ref->getClass();
527
528 if (!target_class->isAssignableFrom(source_class)) {
529 throwNewJavaException(
530 "java/lang/ClassCastException",
531 "Tried to cast from %s to %s.",
532 source_class->toString().c_str(),
533 jtype_traits<T>::kBaseName.c_str());
534 }
535
536 return static_ref_cast<T>(ref);
537 }
538
539 } // namespace jni
540 } // namespace facebook
541