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 /** @file References.h
18 *
19 * Functionality similar to smart pointers, but for references into the VM. Four
20 * main reference types are provided: local_ref, global_ref, weak_ref, and
21 * alias_ref. All are generic templates that and refer to objects in the jobject
22 * hierarchy. The type of the referred objects are specified using the template
23 * parameter. All reference types except alias_ref own their underlying
24 * reference, just as a std smart pointer owns the underlying raw pointer. In
25 * the context of std smart pointers, these references behave like unique_ptr,
26 * and have basically the same interface. Thus, when the reference is
27 * destructed, the plain JNI reference, i.e. the underlying JNI reference (like
28 * the parameters passed directly to JNI functions), is released. The alias
29 * references provides no ownership and is a simple wrapper for plain JNI
30 * references.
31 *
32 * All but the weak references provides access to the underlying object using
33 * dereferencing, and a get() method. It is also possible to convert these
34 * references to booleans to test for nullity. To access the underlying object
35 * of a weak reference, the reference must either be released, or the weak
36 * reference can be used to create a local or global reference.
37 *
38 * An owning reference is created either by moving the reference from an
39 * existing owned reference, by copying an existing owned reference (which
40 * creates a new underlying reference), by using the default constructor which
41 * initialize the reference to nullptr, or by using a helper function. The
42 * helper function exist in two flavors: make_XXX or adopt_XXX.
43 *
44 * Adopting takes a plain JNI reference and wrap it in an owned reference. It
45 * takes ownership of the plain JNI reference so be sure that no one else owns
46 * the reference when you adopt it, and make sure that you know what kind of
47 * reference it is.
48 *
49 * New owned references can be created from existing plain JNI references, alias
50 * references, local references, and global references (i.e. non-weak
51 * references) using the make_local, make_global, and make_weak functions.
52 *
53 * Alias references can be implicitly initialized using global, local and plain
54 * JNI references using the wrap_alias function. Here, we don't assume ownership
55 * of the passed-in reference, but rather create a separate reference that we do
56 * own, leaving the passed-in reference to its fate.
57 *
58 * Similar rules apply for assignment. An owned reference can be copy or move
59 * assigned using a smart reference of the same type. In the case of copy
60 * assignment a new reference is created. Alias reference can also be assigned
61 * new values, but since they are simple wrappers of plain JNI references there
62 * is no move semantics involved.
63 *
64 * Alias references are special in that they do not own the object and can
65 * therefore safely be converted to and from its corresponding plain JNI
66 * reference. They are useful as parameters of functions that do not affect the
67 * lifetime of a reference. Usage can be compared with using plain JNI pointers
68 * as parameters where a function does not take ownership of the underlying
69 * object.
70 *
71 * The local, global, and alias references makes it possible to access methods
72 * in the underlying objects. A core set of classes are implemented in
73 * CoreClasses.h, and user defined wrappers are supported (see example below).
74 * The wrappers also supports inheritance so a wrapper can inherit from another
75 * wrapper to gain access to its functionality. As an example the jstring
76 * wrapper inherits from the jobject wrapper, so does the jclass wrapper. That
77 * means that you can for example call the toString() method using the jclass
78 * wrapper, or any other class that inherits from the jobject wrapper.
79 *
80 * Note that the wrappers are parameterized on the static type of your (jobject)
81 * pointer, thus if you have a jobject that refers to a Java String you will
82 * need to cast it to jstring to get the jstring wrapper. This also mean that if
83 * you make a down cast that is invalid there will be no one stopping you and
84 * the wrappers currently does not detect this which can cause crashes. Thus,
85 * cast wisely.
86 *
87 * @include WrapperSample.cpp
88 */
89
90 #pragma once
91
92 #include <cassert>
93 #include <cstddef>
94 #include <type_traits>
95
96 #include <jni.h>
97
98 #include "ReferenceAllocators.h"
99 #include "References-forward.h"
100 #include "TypeTraits.h"
101
102 namespace facebook {
103 namespace jni {
104
105 /// Convenience function to wrap an existing local reference
106 template <typename T>
107 local_ref<T> adopt_local(T ref) noexcept;
108
109 /// Convenience function to wrap an existing global reference
110 template <typename T>
111 global_ref<T> adopt_global(T ref) noexcept;
112
113 /// Convenience function to wrap an existing weak reference
114 template <typename T>
115 weak_ref<T> adopt_weak_global(T ref) noexcept;
116
117 /// Swaps two owning references of the same type
118 template <typename T>
119 void swap(weak_ref<T>& a, weak_ref<T>& b) noexcept;
120
121 /// Swaps two owning references of the same type
122 template <typename T, typename Alloc>
123 void swap(
124 basic_strong_ref<T, Alloc>& a,
125 basic_strong_ref<T, Alloc>& b) noexcept;
126
127 /**
128 * Retrieve the plain reference from a plain reference.
129 */
130 template <typename T>
131 enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref);
132
133 /**
134 * Retrieve the plain reference from an alias reference.
135 */
136 template <typename T>
137 JniType<T> getPlainJniReference(alias_ref<T> ref);
138
139 /**
140 * Retrieve the plain JNI reference from any reference owned reference.
141 */
142 template <typename T, typename Alloc>
143 JniType<T> getPlainJniReference(const base_owned_ref<T, Alloc>& ref);
144
145 class JObject;
146 class JClass;
147
148 namespace detail {
149
150 template <typename T>
IsJavaClassType()151 constexpr bool IsJavaClassType() {
152 return std::is_base_of<JObject, T>::value;
153 }
154
155 template <typename T, typename Enable = void>
156 struct HasJniRefRepr : std::false_type {};
157
158 template <typename T>
159 struct HasJniRefRepr<
160 T,
161 typename std::enable_if<
162 !std::is_same<typename T::JniRefRepr, void>::value,
163 void>::type> : std::true_type {
164 using type = typename T::JniRefRepr;
165 };
166
167 template <typename T>
168 struct RefReprType<T*> {
169 static_assert(HasJniRefRepr<T>::value, "Repr type missing JniRefRepr.");
170 using type = typename HasJniRefRepr<T>::type;
171 static_assert(IsJavaClassType<type>(), "Repr type missing JObject base.");
172 static_assert(
173 std::is_same<type, typename RefReprType<type>::type>::value,
174 "RefReprType<T> not idempotent");
175 };
176
177 template <typename T>
178 struct RefReprType<
179 T,
180 typename std::enable_if<IsJavaClassType<T>(), void>::type> {
181 using type = T;
182 static_assert(IsJavaClassType<type>(), "Repr type missing JObject base.");
183 static_assert(
184 std::is_same<type, typename RefReprType<type>::type>::value,
185 "RefReprType<T> not idempotent");
186 };
187
188 template <typename T>
189 struct JavaObjectType {
190 using type = typename RefReprType<T>::type::javaobject;
191 static_assert(
192 IsPlainJniReference<type>(),
193 "JavaObjectType<T> not a plain jni reference");
194 static_assert(
195 std::is_same<type, typename JavaObjectType<type>::type>::value,
196 "JavaObjectType<T> not idempotent");
197 };
198
199 template <typename T>
200 struct JavaObjectType<T*> {
201 using type = T*;
202 static_assert(
203 IsPlainJniReference<type>(),
204 "JavaObjectType<T> not a plain jni reference");
205 static_assert(
206 std::is_same<type, typename JavaObjectType<type>::type>::value,
207 "JavaObjectType<T> not idempotent");
208 };
209
210 template <typename T>
211 struct PrimitiveOrJavaObjectType<T, enable_if_t<IsJniPrimitive<T>(), void>> {
212 using type = T;
213 static_assert(
214 IsJniPrimitive<type>(),
215 "PrimitiveOrJavaObjectType<T> not a jni primitive");
216 static_assert(
217 std::is_same<type, typename PrimitiveOrJavaObjectType<type>::type>::value,
218 "PrimitiveOrJavaObjectType<T> not idempotent");
219 };
220
221 template <typename T>
222 struct PrimitiveOrJavaObjectType<
223 T,
224 enable_if_t<IsPlainJniReference<T>(), void>> {
225 using type = T;
226 static_assert(
227 IsPlainJniReference<type>(),
228 "PrimitiveOrJavaObjectType<T> not a plain jni reference");
229 static_assert(
230 std::is_same<type, typename PrimitiveOrJavaObjectType<type>::type>::value,
231 "PrimitiveOrJavaObjectType<T> not idempotent");
232 };
233
234 template <typename T>
235 struct PrimitiveOrJavaObjectType<T, enable_if_t<IsJavaClassType<T>(), void>> {
236 using type = JniType<T>;
237 static_assert(
238 IsPlainJniReference<type>(),
239 "PrimitiveOrJavaObjectType<T> not a plain jni reference");
240 static_assert(
241 std::is_same<type, typename PrimitiveOrJavaObjectType<type>::type>::value,
242 "PrimitiveOrJavaObjectType<T> not idempotent");
243 };
244
245 template <typename Repr>
246 struct ReprStorage {
247 explicit ReprStorage(JniType<Repr> obj) noexcept;
248
249 void set(JniType<Repr> obj) noexcept;
250
251 Repr& get() noexcept;
252 const Repr& get() const noexcept;
253 JniType<Repr> jobj() const noexcept;
254
255 void swap(ReprStorage& other) noexcept;
256
257 ReprStorage() = delete;
258 ReprStorage(const ReprStorage&) = delete;
259 ReprStorage(ReprStorage&&) = delete;
260 ReprStorage& operator=(const ReprStorage&) = delete;
261 ReprStorage& operator=(ReprStorage&&) = delete;
262
263 private:
264 using Storage = typename std::
265 aligned_storage<sizeof(JObjectBase), alignof(JObjectBase)>::type;
266 Storage storage_;
267 };
268
269 } // namespace detail
270
271 /**
272 * Create a new local reference from an existing reference
273 *
274 * @param ref a plain JNI, alias, or strong reference
275 * @return an owned local reference (referring to null if the input does)
276 * @throws std::bad_alloc if the JNI reference could not be created
277 */
278 template <typename T>
279 enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
280 make_local(const T& ref);
281
282 /**
283 * Create a new global reference from an existing reference
284 *
285 * @param ref a plain JNI, alias, or strong reference
286 * @return an owned global reference (referring to null if the input does)
287 * @throws std::bad_alloc if the JNI reference could not be created
288 */
289 template <typename T>
290 enable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>
291 make_global(const T& ref);
292
293 /**
294 * Create a new weak global reference from an existing reference
295 *
296 * @param ref a plain JNI, alias, or strong reference
297 * @return an owned weak global reference (referring to null if the input does)
298 * @throws std::bad_alloc if the returned reference is null
299 */
300 template <typename T>
301 enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
302 make_weak(const T& ref);
303
304 /**
305 * Compare two references to see if they refer to the same object
306 */
307 template <typename T1, typename T2>
308 enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
309 operator==(const T1& a, const T2& b);
310
311 /**
312 * Compare two references to see if they don't refer to the same object
313 */
314 template <typename T1, typename T2>
315 enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
316 operator!=(const T1& a, const T2& b);
317
318 /**
319 * Compare references against nullptr
320 */
321 template <typename T1>
322 enable_if_t<IsNonWeakReference<T1>(), bool> operator==(
323 const T1& a,
324 std::nullptr_t);
325
326 template <typename T1>
327 enable_if_t<IsNonWeakReference<T1>(), bool> operator==(
328 std::nullptr_t,
329 const T1& a);
330
331 template <typename T1>
332 enable_if_t<IsNonWeakReference<T1>(), bool> operator!=(
333 const T1& a,
334 std::nullptr_t);
335
336 template <typename T1>
337 enable_if_t<IsNonWeakReference<T1>(), bool> operator!=(
338 std::nullptr_t,
339 const T1& a);
340
341 template <typename T, typename Alloc>
342 class base_owned_ref {
343 public:
344 using javaobject = JniType<T>;
345
346 /**
347 * Release the ownership and set the reference to null. Thus no deleter is
348 * invoked.
349 * @return Returns the reference
350 */
351 javaobject release() noexcept;
352
353 /**
354 * Reset the reference to refer to nullptr.
355 */
356 void reset() noexcept;
357
358 protected:
359 using Repr = ReprType<T>;
360 detail::ReprStorage<Repr> storage_;
361
362 javaobject get() const noexcept;
363 void set(javaobject ref) noexcept;
364
365 /*
366 * Wrap an existing reference and transfers its ownership to the newly created
367 * unique reference. NB! Does not create a new reference
368 */
369 explicit base_owned_ref(javaobject reference) noexcept;
370
371 /// Create a null reference
372 base_owned_ref() noexcept;
373
374 /// Create a null reference
375 explicit base_owned_ref(std::nullptr_t) noexcept;
376
377 /// Copy constructor (note creates a new reference)
378 base_owned_ref(const base_owned_ref& other);
379 template <typename U>
380 base_owned_ref(const base_owned_ref<U, Alloc>& other);
381
382 /// Transfers ownership of an underlying reference from one unique reference
383 /// to another
384 base_owned_ref(base_owned_ref&& other) noexcept;
385 template <typename U>
386 base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept;
387
388 /// The delete the underlying reference if applicable
389 ~base_owned_ref() noexcept;
390
391 /// Assignment operator (note creates a new reference)
392 base_owned_ref& operator=(const base_owned_ref& other);
393
394 /// Assignment by moving a reference thus not creating a new reference
395 base_owned_ref& operator=(base_owned_ref&& rhs) noexcept;
396
397 void reset(javaobject reference) noexcept;
398
399 friend javaobject jni::getPlainJniReference<>(
400 const base_owned_ref<T, Alloc>& ref);
401
402 template <typename U, typename UAlloc>
403 friend class base_owned_ref;
404 };
405
406 /**
407 * A smart reference that owns its underlying JNI reference. The class provides
408 * basic functionality to handle a reference but gives no access to it unless
409 * the reference is released, thus no longer owned. The API is stolen with pride
410 * from unique_ptr and the semantics should be basically the same. This class
411 * should not be used directly, instead use
412 * @ref weak_ref
413 */
414 template <typename T>
415 class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
416 public:
417 using javaobject = JniType<T>;
418
419 using Allocator = WeakGlobalReferenceAllocator;
420
421 // This inherits non-default, non-copy, non-move ctors.
422 using base_owned_ref<T, Allocator>::base_owned_ref;
423
424 /// Create a null reference
425 weak_ref() noexcept : base_owned_ref<T, Allocator>{} {}
426
427 /// Create a null reference
428 /* implicit */ weak_ref(std::nullptr_t) noexcept
429 : base_owned_ref<T, Allocator>{nullptr} {}
430
431 /// Copy constructor (note creates a new reference)
432 weak_ref(const weak_ref& other) : base_owned_ref<T, Allocator>{other} {}
433
434 // This needs to be explicit to change its visibility.
435 template <typename U>
436 weak_ref(const weak_ref<U>& other) : base_owned_ref<T, Allocator>{other} {}
437
438 /// Transfers ownership of an underlying reference from one unique reference
439 /// to another
440 weak_ref(weak_ref&& other) noexcept
441 : base_owned_ref<T, Allocator>{std::move(other)} {}
442
443 // Move from ref to compatible type.
444 template <typename U>
445 weak_ref(weak_ref<U>&& other)
446 : base_owned_ref<T, Allocator>{std::move(other)} {}
447
448 /// Assignment operator (note creates a new reference)
449 weak_ref& operator=(const weak_ref& other);
450
451 /// Assignment by moving a reference thus not creating a new reference
452 weak_ref& operator=(weak_ref&& rhs) noexcept;
453
454 // Creates an owned local reference to the referred object or to null if the
455 // object is reclaimed
456 local_ref<T> lockLocal() const;
457
458 // Creates an owned global reference to the referred object or to null if the
459 // object is reclaimed
460 global_ref<T> lockGlobal() const;
461
462 private:
463 // get/release/reset on weak_ref are not exposed to users.
464 using base_owned_ref<T, Allocator>::get;
465 using base_owned_ref<T, Allocator>::release;
466 using base_owned_ref<T, Allocator>::reset;
467 /*
468 * Wrap an existing reference and transfers its ownership to the newly created
469 * unique reference. NB! Does not create a new reference
470 */
471 explicit weak_ref(javaobject reference) noexcept
472 : base_owned_ref<T, Allocator>{reference} {}
473
474 template <typename T2>
475 friend class weak_ref;
476 friend weak_ref<javaobject> adopt_weak_global<javaobject>(
477 javaobject ref) noexcept;
478 friend void swap<T>(weak_ref& a, weak_ref& b) noexcept;
479 };
480
481 /**
482 * A class representing owned strong references to Java objects. This class
483 * should not be used directly, instead use @ref local_ref, or @ref global_ref.
484 */
485 template <typename T, typename Alloc>
486 class basic_strong_ref : public base_owned_ref<T, Alloc> {
487 using typename base_owned_ref<T, Alloc>::Repr;
488
489 public:
490 using javaobject = JniType<T>;
491
492 using Allocator = Alloc;
493
494 // This inherits non-default, non-copy, non-move ctors.
495 using base_owned_ref<T, Alloc>::base_owned_ref;
496 using base_owned_ref<T, Alloc>::release;
497 using base_owned_ref<T, Alloc>::reset;
498
499 /// Create a null reference
500 basic_strong_ref() noexcept : base_owned_ref<T, Alloc>{} {}
501
502 /// Create a null reference
503 /* implicit */ basic_strong_ref(std::nullptr_t) noexcept
504 : base_owned_ref<T, Alloc>{nullptr} {}
505
506 /// Copy constructor (note creates a new reference)
507 basic_strong_ref(const basic_strong_ref& other)
508 : base_owned_ref<T, Alloc>{other} {}
509
510 // This needs to be explicit to change its visibility.
511 template <typename U>
512 basic_strong_ref(const basic_strong_ref<U, Alloc>& other)
513 : base_owned_ref<T, Alloc>{other} {}
514
515 // Move from ref to compatible type.
516 template <typename U>
517 basic_strong_ref(basic_strong_ref<U, Alloc>&& other)
518 : base_owned_ref<T, Alloc>{std::move(other)} {}
519
520 /// Transfers ownership of an underlying reference from one unique reference
521 /// to another
522 basic_strong_ref(basic_strong_ref&& other) noexcept
523 : base_owned_ref<T, Alloc>{std::move(other)} {}
524
525 /// Assignment operator (note creates a new reference)
526 basic_strong_ref& operator=(const basic_strong_ref& other);
527
528 /// Assignment by moving a reference thus not creating a new reference
529 basic_strong_ref& operator=(basic_strong_ref&& rhs) noexcept;
530
531 /// Get the plain JNI reference
532 using base_owned_ref<T, Allocator>::get;
533
534 /// Release the ownership of the reference and return the wrapped reference in
535 /// an alias
536 alias_ref<T> releaseAlias() noexcept;
537
538 /// Checks if the reference points to a non-null object
539 explicit operator bool() const noexcept;
540
541 /// Access the functionality provided by the object wrappers
542 Repr* operator->() noexcept;
543
544 /// Access the functionality provided by the object wrappers
545 const Repr* operator->() const noexcept;
546
547 /// Provide a reference to the underlying wrapper (be sure that it is non-null
548 /// before invoking)
549 Repr& operator*() noexcept;
550
551 /// Provide a const reference to the underlying wrapper (be sure that it is
552 /// non-null before invoking)
553 const Repr& operator*() const noexcept;
554
555 private:
556 using base_owned_ref<T, Alloc>::storage_;
557
558 /*
559 * Wrap an existing reference and transfers its ownership to the newly created
560 * unique reference. NB! Does not create a new reference
561 */
562 explicit basic_strong_ref(javaobject reference) noexcept
563 : base_owned_ref<T, Alloc>{reference} {}
564
565 friend local_ref<T> adopt_local<T>(T ref) noexcept;
566 friend global_ref<T> adopt_global<T>(T ref) noexcept;
567 friend void swap<T, Alloc>(basic_strong_ref& a, basic_strong_ref& b) noexcept;
568 };
569
570 template <typename T>
571 enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
572
573 /// Swaps to alias reference of the same type
574 template <typename T>
575 void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept;
576
577 /**
578 * A non-owning variant of the smart references (a dumb
579 * reference). Use this representation when you don't want to claim
580 * ownership of the underlying reference (compare to using raw
581 * pointers instead of smart pointers.)
582 */
583 template <typename T>
584 class alias_ref {
585 using Repr = ReprType<T>;
586
587 public:
588 using javaobject = JniType<T>;
589
590 /// Create a null reference
591 alias_ref() noexcept;
592
593 /// Create a null reference
594 /* implicit */ alias_ref(std::nullptr_t) noexcept;
595
596 /// Copy constructor
597 alias_ref(const alias_ref& other) noexcept;
598
599 /// Wrap an existing plain JNI reference
600 /* implicit */ alias_ref(javaobject ref) noexcept;
601
602 /// Wrap an existing smart reference of any type convertible to T
603 template <
604 typename TOther,
605 typename = enable_if_t<IsConvertible<JniType<TOther>, javaobject>(), T>>
606 alias_ref(alias_ref<TOther> other) noexcept;
607
608 /// Wrap an existing alias reference of a type convertible to T
609 template <
610 typename TOther,
611 typename AOther,
612 typename = enable_if_t<IsConvertible<JniType<TOther>, javaobject>(), T>>
613 alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept;
614
615 /// Assignment operator
616 alias_ref& operator=(alias_ref other) noexcept;
617
618 /// Checks if the reference points to a non-null object
619 explicit operator bool() const noexcept;
620
621 /// Converts back to a plain JNI reference
622 javaobject get() const noexcept;
623
624 /// Access the functionality provided by the object wrappers
625 Repr* operator->() noexcept;
626
627 /// Access the functionality provided by the object wrappers
628 const Repr* operator->() const noexcept;
629
630 /// Provide a guaranteed non-null reference (be sure that it is non-null
631 /// before invoking)
632 Repr& operator*() noexcept;
633
634 /// Provide a guaranteed non-null reference (be sure that it is non-null
635 /// before invoking)
636 const Repr& operator*() const noexcept;
637
638 private:
639 void set(javaobject ref) noexcept;
640
641 detail::ReprStorage<Repr> storage_;
642
643 friend void swap<T>(alias_ref& a, alias_ref& b) noexcept;
644 };
645
646 /**
647 * RAII object to create a local JNI frame, using PushLocalFrame/PopLocalFrame.
648 *
649 * This is useful when you have a call which is initiated from C++-land, and
650 * therefore doesn't automatically get a local JNI frame managed for you by the
651 * JNI framework.
652 */
653 class JniLocalScope {
654 public:
655 JniLocalScope(JNIEnv* p_env, jint capacity);
656 ~JniLocalScope();
657
658 private:
659 JNIEnv* env_;
660 bool hasFrame_;
661 };
662
663 template <typename T, typename U>
664 enable_if_t<IsPlainJniReference<JniType<T>>(), local_ref<T>> static_ref_cast(
665 const local_ref<U>& ref) noexcept;
666
667 template <typename T, typename U>
668 enable_if_t<IsPlainJniReference<JniType<T>>(), global_ref<T>> static_ref_cast(
669 const global_ref<U>& ref) noexcept;
670
671 template <typename T, typename U>
672 enable_if_t<IsPlainJniReference<JniType<T>>(), alias_ref<T>> static_ref_cast(
673 const alias_ref<U>& ref) noexcept;
674
675 template <typename T, typename RefType>
676 auto dynamic_ref_cast(const RefType& ref)
677 -> enable_if_t<
678 IsPlainJniReference<JniType<T>>(),
679 decltype(static_ref_cast<T>(ref))>;
680
681 } // namespace jni
682 } // namespace facebook
683
684 #include "References-inl.h"
685