xref: /aosp_15_r20/external/fbjni/cxx/fbjni/detail/References-inl.h (revision 65c59e023c5336bbd4a23be7af78407e3d80e7e7)
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