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 <stdlib.h>
20 #include <string.h>
21 #include <type_traits>
22
23 #include "Common.h"
24 #include "Exceptions.h"
25 #include "Meta.h"
26 #include "MetaConvert.h"
27
28 namespace facebook {
29 namespace jni {
30
31 // jobject
32 // /////////////////////////////////////////////////////////////////////////////////////////
33
isSameObject(alias_ref<JObject> lhs,alias_ref<JObject> rhs)34 inline bool isSameObject(
35 alias_ref<JObject> lhs,
36 alias_ref<JObject> rhs) noexcept {
37 return Environment::current()->IsSameObject(lhs.get(), rhs.get()) !=
38 JNI_FALSE;
39 }
40
getClass()41 inline local_ref<JClass> JObject::getClass() const noexcept {
42 return adopt_local(Environment::current()->GetObjectClass(self()));
43 }
44
isInstanceOf(alias_ref<JClass> cls)45 inline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept {
46 return Environment::current()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
47 }
48
49 template <typename T>
getFieldValue(JField<T> field)50 inline T JObject::getFieldValue(JField<T> field) const noexcept {
51 return field.get(self());
52 }
53
54 template <typename T>
getFieldValue(JField<T * > field)55 inline local_ref<T*> JObject::getFieldValue(JField<T*> field) const noexcept {
56 return adopt_local(field.get(self()));
57 }
58
59 template <typename T>
setFieldValue(JField<T> field,T value)60 inline void JObject::setFieldValue(JField<T> field, T value) noexcept {
61 field.set(self(), value);
62 }
63
64 template <typename T, typename>
setFieldValue(JField<T> field,alias_ref<T> value)65 inline void JObject::setFieldValue(
66 JField<T> field,
67 alias_ref<T> value) noexcept {
68 setFieldValue(field, value.get());
69 }
70
toString()71 inline std::string JObject::toString() const {
72 static const auto method =
73 findClassLocal("java/lang/Object")->getMethod<jstring()>("toString");
74
75 return method(self())->toStdString();
76 }
77
78 // Class is here instead of CoreClasses.h because we need
79 // alias_ref to be complete.
80 class MonitorLock {
81 public:
82 inline MonitorLock() noexcept;
83 inline MonitorLock(alias_ref<JObject> object) noexcept;
84 inline ~MonitorLock() noexcept;
85
86 inline MonitorLock(MonitorLock&& other) noexcept;
87 inline MonitorLock& operator=(MonitorLock&& other) noexcept;
88
89 inline MonitorLock(const MonitorLock&) = delete;
90 inline MonitorLock& operator=(const MonitorLock&) = delete;
91
92 private:
93 inline void reset() noexcept;
94 alias_ref<JObject> owned_;
95 };
96
MonitorLock()97 MonitorLock::MonitorLock() noexcept : owned_(nullptr) {}
98
MonitorLock(alias_ref<JObject> object)99 MonitorLock::MonitorLock(alias_ref<JObject> object) noexcept : owned_(object) {
100 Environment::current()->MonitorEnter(object.get());
101 }
102
reset()103 void MonitorLock::reset() noexcept {
104 if (owned_) {
105 Environment::current()->MonitorExit(owned_.get());
106 if (Environment::current()->ExceptionCheck()) {
107 abort(); // Lock mismatch
108 }
109 owned_ = nullptr;
110 }
111 }
112
~MonitorLock()113 MonitorLock::~MonitorLock() noexcept {
114 reset();
115 }
116
MonitorLock(MonitorLock && other)117 MonitorLock::MonitorLock(MonitorLock&& other) noexcept : owned_(other.owned_) {
118 other.owned_ = nullptr;
119 }
120
121 MonitorLock& MonitorLock::operator=(MonitorLock&& other) noexcept {
122 reset();
123 owned_ = other.owned_;
124 other.owned_ = nullptr;
125 return *this;
126 }
127
lock()128 inline MonitorLock JObject::lock() const noexcept {
129 return MonitorLock(this_);
130 }
131
self()132 inline jobject JObject::self() const noexcept {
133 return this_;
134 }
135
swap(JObject & a,JObject & b)136 inline void swap(JObject& a, JObject& b) noexcept {
137 using std::swap;
138 swap(a.this_, b.this_);
139 }
140
141 // JavaClass
142 // ///////////////////////////////////////////////////////////////////////////////////////
143
144 namespace detail {
145 template <typename JC, typename... Args>
newInstance(Args...args)146 static local_ref<JC> newInstance(Args... args) {
147 static auto cls = JC::javaClassStatic();
148 static const auto constructor =
149 cls->template getConstructor<typename JC::javaobject(Args...)>();
150 return cls->newObject(constructor, args...);
151 }
152 } // namespace detail
153
154 template <typename T, typename B, typename J>
155 auto JavaClass<T, B, J>::self() const noexcept -> javaobject {
156 return static_cast<javaobject>(JObject::self());
157 }
158
159 // jclass
160 // //////////////////////////////////////////////////////////////////////////////////////////
161
getSuperclass()162 inline local_ref<JClass> JClass::getSuperclass() const noexcept {
163 return adopt_local(Environment::current()->GetSuperclass(self()));
164 }
165
registerNatives(std::initializer_list<JNINativeMethod> methods)166 inline void JClass::registerNatives(
167 std::initializer_list<JNINativeMethod> methods) {
168 const auto env = Environment::current();
169 auto result = env->RegisterNatives(
170 self(), methods.begin(), static_cast<int>(methods.size()));
171 FACEBOOK_JNI_THROW_EXCEPTION_IF(result != JNI_OK);
172 }
173
isAssignableFrom(alias_ref<JClass> cls)174 inline bool JClass::isAssignableFrom(alias_ref<JClass> cls) const noexcept {
175 const auto env = Environment::current();
176 // Ths method has behavior compatible with the
177 // java.lang.Class#isAssignableFrom method. The order of the
178 // arguments to the JNI IsAssignableFrom C function is "opposite"
179 // from what some might expect, which makes this code look a little
180 // odd, but it is correct.
181 const auto result = env->IsAssignableFrom(cls.get(), self());
182 return result;
183 }
184
185 template <typename F>
getConstructor()186 inline JConstructor<F> JClass::getConstructor() const {
187 return getConstructor<F>(
188 jmethod_traits_from_cxx<F>::kConstructorDescriptor.c_str());
189 }
190
191 template <typename F>
getConstructor(const char * descriptor)192 inline JConstructor<F> JClass::getConstructor(const char* descriptor) const {
193 constexpr auto constructor_method_name = "<init>";
194 return getMethod<F>(constructor_method_name, descriptor);
195 }
196
197 template <typename F>
getMethod(const char * name)198 inline JMethod<F> JClass::getMethod(const char* name) const {
199 return getMethod<F>(name, jmethod_traits_from_cxx<F>::kDescriptor.c_str());
200 }
201
202 template <typename F>
getMethod(const char * name,const char * descriptor)203 inline JMethod<F> JClass::getMethod(const char* name, const char* descriptor)
204 const {
205 const auto env = Environment::current();
206 const auto method = env->GetMethodID(self(), name, descriptor);
207 FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
208 return JMethod<F>{method};
209 }
210
211 template <typename F>
getStaticMethod(const char * name)212 inline JStaticMethod<F> JClass::getStaticMethod(const char* name) const {
213 return getStaticMethod<F>(
214 name, jmethod_traits_from_cxx<F>::kDescriptor.c_str());
215 }
216
217 template <typename F>
getStaticMethod(const char * name,const char * descriptor)218 inline JStaticMethod<F> JClass::getStaticMethod(
219 const char* name,
220 const char* descriptor) const {
221 const auto env = Environment::current();
222 const auto method = env->GetStaticMethodID(self(), name, descriptor);
223 FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
224 return JStaticMethod<F>{method};
225 }
226
227 template <typename F>
getNonvirtualMethod(const char * name)228 inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(
229 const char* name) const {
230 return getNonvirtualMethod<F>(
231 name, jmethod_traits_from_cxx<F>::kDescriptor.c_str());
232 }
233
234 template <typename F>
getNonvirtualMethod(const char * name,const char * descriptor)235 inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(
236 const char* name,
237 const char* descriptor) const {
238 const auto env = Environment::current();
239 const auto method = env->GetMethodID(self(), name, descriptor);
240 FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
241 return JNonvirtualMethod<F>{method};
242 }
243
244 template <typename T>
getField(const char * name)245 inline JField<PrimitiveOrJniType<T>> JClass::getField(const char* name) const {
246 return getField<T>(name, jtype_traits<T>::kDescriptor.c_str());
247 }
248
249 template <typename T>
getField(const char * name,const char * descriptor)250 inline JField<PrimitiveOrJniType<T>> JClass::getField(
251 const char* name,
252 const char* descriptor) const {
253 const auto env = Environment::current();
254 auto field = env->GetFieldID(self(), name, descriptor);
255 FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
256 return JField<PrimitiveOrJniType<T>>{field};
257 }
258
259 template <typename T>
getStaticField(const char * name)260 inline JStaticField<PrimitiveOrJniType<T>> JClass::getStaticField(
261 const char* name) const {
262 return getStaticField<T>(name, jtype_traits<T>::kDescriptor.c_str());
263 }
264
265 template <typename T>
getStaticField(const char * name,const char * descriptor)266 inline JStaticField<PrimitiveOrJniType<T>> JClass::getStaticField(
267 const char* name,
268 const char* descriptor) const {
269 const auto env = Environment::current();
270 auto field = env->GetStaticFieldID(self(), name, descriptor);
271 FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
272 return JStaticField<PrimitiveOrJniType<T>>{field};
273 }
274
275 template <typename T>
getStaticFieldValue(JStaticField<T> field)276 inline T JClass::getStaticFieldValue(JStaticField<T> field) const noexcept {
277 return field.get(self());
278 }
279
280 template <typename T>
getStaticFieldValue(JStaticField<T * > field)281 inline local_ref<T*> JClass::getStaticFieldValue(
282 JStaticField<T*> field) noexcept {
283 return adopt_local(field.get(self()));
284 }
285
286 template <typename T>
setStaticFieldValue(JStaticField<T> field,T value)287 inline void JClass::setStaticFieldValue(
288 JStaticField<T> field,
289 T value) noexcept {
290 field.set(self(), value);
291 }
292
293 template <typename T, typename>
setStaticFieldValue(JStaticField<T> field,alias_ref<T> value)294 inline void JClass::setStaticFieldValue(
295 JStaticField<T> field,
296 alias_ref<T> value) noexcept {
297 setStaticFieldValue(field, value.get());
298 }
299
300 template <typename R, typename... Args>
newObject(JConstructor<R (Args...)> constructor,Args...args)301 inline local_ref<R> JClass::newObject(
302 JConstructor<R(Args...)> constructor,
303 Args... args) const {
304 const auto env = Environment::current();
305 auto object = env->NewObject(
306 self(),
307 constructor.getId(),
308 detail::callToJni(
309 detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
310 FACEBOOK_JNI_THROW_EXCEPTION_IF(!object);
311 return adopt_local(static_cast<R>(object));
312 }
313
self()314 inline jclass JClass::self() const noexcept {
315 return static_cast<jclass>(JObject::self());
316 }
317
registerNatives(const char * name,std::initializer_list<JNINativeMethod> methods)318 inline void registerNatives(
319 const char* name,
320 std::initializer_list<JNINativeMethod> methods) {
321 findClassLocal(name)->registerNatives(methods);
322 }
323
324 inline auto JClass::getCanonicalName() -> local_ref<JString> {
325 static auto meth =
326 javaClassStatic()->getMethod<JString::javaobject()>("getCanonicalName");
327 return meth(self());
328 }
329
330 // jstring
331 // /////////////////////////////////////////////////////////////////////////////////////////
332
make_jstring(const std::string & utf8)333 inline local_ref<JString> make_jstring(const std::string& utf8) {
334 return make_jstring(utf8.c_str());
335 }
336
337 namespace detail {
338 // convert to std::string from jstring
339 template <>
340 struct Convert<std::string> {
341 typedef jstring jniType;
342 static std::string fromJni(jniType t) {
343 return wrap_alias(t)->toStdString();
344 }
345 static jniType toJniRet(const std::string& t) {
346 return make_jstring(t).release();
347 }
348 static local_ref<JString> toCall(const std::string& t) {
349 return make_jstring(t);
350 }
351 };
352
353 // convert return from const char*
354 template <>
355 struct Convert<const char*> {
356 typedef jstring jniType;
357 // no automatic synthesis of const char*. (It can't be freed.)
358 static jniType toJniRet(const char* t) {
359 return make_jstring(t).release();
360 }
361 static local_ref<JString> toCall(const char* t) {
362 return make_jstring(t);
363 }
364 };
365 } // namespace detail
366
367 // jtypeArray
368 // //////////////////////////////////////////////////////////////////////////////////////
369
370 namespace detail {
371 inline size_t JArray::size() const noexcept {
372 const auto env = Environment::current();
373 return env->GetArrayLength(self());
374 }
375 } // namespace detail
376
377 namespace detail {
378 template <typename Target>
379 inline ElementProxy<Target>::ElementProxy(Target* target, size_t idx)
380 : target_{target}, idx_{idx} {}
381
382 template <typename Target>
383 inline ElementProxy<Target>& ElementProxy<Target>::operator=(const T& o) {
384 target_->setElement(idx_, o);
385 return *this;
386 }
387
388 template <typename Target>
389 inline ElementProxy<Target>& ElementProxy<Target>::operator=(
390 alias_ref<typename Target::javaentry>& o) {
391 target_->setElement(idx_, o.get());
392 return *this;
393 }
394
395 template <typename Target>
396 inline ElementProxy<Target>& ElementProxy<Target>::operator=(
397 alias_ref<typename Target::javaentry>&& o) {
398 target_->setElement(idx_, o.get());
399 return *this;
400 }
401
402 template <typename Target>
403 inline ElementProxy<Target>& ElementProxy<Target>::operator=(
404 const ElementProxy<Target>& o) {
405 auto src = o.target_->getElement(o.idx_);
406 target_->setElement(idx_, src.get());
407 return *this;
408 }
409
410 template <typename Target>
411 inline ElementProxy<Target>::ElementProxy::operator const local_ref<
412 typename Target::javaentry>() const {
413 return target_->getElement(idx_);
414 }
415
416 template <typename Target>
417 inline ElementProxy<Target>::ElementProxy::operator local_ref<
418 typename Target::javaentry>() {
419 return target_->getElement(idx_);
420 }
421 } // namespace detail
422
423 template <typename T>
424 auto JArrayClass<T>::newArray(size_t count) -> local_ref<javaobject> {
425 static const auto elementClass =
426 findClassStatic(jtype_traits<T>::kBaseName.c_str());
427 const auto env = Environment::current();
428 auto rawArray = env->NewObjectArray(
429 static_cast<jsize>(count), elementClass.get(), nullptr);
430 FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);
431 return adopt_local(static_cast<javaobject>(rawArray));
432 }
433
434 template <typename T>
435 inline void JArrayClass<T>::setElement(size_t idx, T value) {
436 const auto env = Environment::current();
437 env->SetObjectArrayElement(
438 this->self(),
439 static_cast<jsize>(idx),
440 detail::toPlainJniReference(value));
441 }
442
443 template <typename T>
444 inline local_ref<T> JArrayClass<T>::getElement(size_t idx) {
445 const auto env = Environment::current();
446 auto rawElement =
447 env->GetObjectArrayElement(this->self(), static_cast<jsize>(idx));
448 return adopt_local(static_cast<JniType<T>>(rawElement));
449 }
450
451 template <typename T>
452 inline detail::ElementProxy<JArrayClass<T>> JArrayClass<T>::operator[](
453 size_t idx) {
454 return detail::ElementProxy<JArrayClass<T>>(this, idx);
455 }
456
457 template <typename T>
458 local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(
459 jobjectArray ref) {
460 return adopt_local(static_cast<typename JArrayClass<T>::javaobject>(ref));
461 }
462
463 // jarray
464 // /////////////////////////////////////////////////////////////////////////////////////////
465
466 template <typename JArrayType>
467 auto JPrimitiveArray<JArrayType>::getRegion(jsize start, jsize length)
468 -> std::unique_ptr<T[]> {
469 auto buf = std::unique_ptr<T[]>{new T[length]};
470 getRegion(start, length, buf.get());
471 return buf;
472 }
473
474 template <typename JArrayType>
475 auto JPrimitiveArray<JArrayType>::pin()
476 -> PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> {
477 return PinnedPrimitiveArray<T, PinnedArrayAlloc<T>>{this->self(), 0, 0};
478 }
479
480 template <typename JArrayType>
481 auto JPrimitiveArray<JArrayType>::pinRegion(jsize start, jsize length)
482 -> PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> {
483 return PinnedPrimitiveArray<T, PinnedRegionAlloc<T>>{
484 this->self(), start, length};
485 }
486
487 template <typename JArrayType>
488 auto JPrimitiveArray<JArrayType>::pinCritical()
489 -> PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> {
490 return PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>>{this->self(), 0, 0};
491 }
492
493 template <typename T>
494 class PinnedArrayAlloc {
495 public:
496 static void allocate(
497 alias_ref<typename jtype_traits<T>::array_type> array,
498 jsize start,
499 jsize length,
500 T** elements,
501 size_t* size,
502 jboolean* isCopy) {
503 (void)start;
504 (void)length;
505 *elements = array->getElements(isCopy);
506 *size = array->size();
507 }
508 static void release(
509 alias_ref<typename jtype_traits<T>::array_type> array,
510 T* elements,
511 jint start,
512 jint size,
513 jint mode) {
514 (void)start;
515 (void)size;
516 array->releaseElements(elements, mode);
517 }
518 };
519
520 template <typename T>
521 class PinnedCriticalAlloc {
522 public:
523 static void allocate(
524 alias_ref<typename jtype_traits<T>::array_type> array,
525 jsize start,
526 jsize length,
527 T** elements,
528 size_t* size,
529 jboolean* isCopy) {
530 (void)start;
531 (void)length;
532 const auto env = Environment::current();
533 *elements =
534 static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy));
535 FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);
536 *size = array->size();
537 }
538 static void release(
539 alias_ref<typename jtype_traits<T>::array_type> array,
540 T* elements,
541 jint start,
542 jint size,
543 jint mode) {
544 (void)start;
545 (void)size;
546 const auto env = Environment::current();
547 env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);
548 }
549 };
550
551 template <typename T>
552 class PinnedRegionAlloc {
553 public:
554 static void allocate(
555 alias_ref<typename jtype_traits<T>::array_type> array,
556 jsize start,
557 jsize length,
558 T** elements,
559 size_t* size,
560 jboolean* isCopy) {
561 auto buf = array->getRegion(start, length);
562 FACEBOOK_JNI_THROW_EXCEPTION_IF(!buf);
563 *elements = buf.release();
564 *size = length;
565 *isCopy = true;
566 }
567 static void release(
568 alias_ref<typename jtype_traits<T>::array_type> array,
569 T* elements,
570 jint start,
571 jint size,
572 jint mode) {
573 std::unique_ptr<T[]> holder;
574 if (mode == 0 || mode == JNI_ABORT) {
575 holder.reset(elements);
576 }
577 if (mode == 0 || mode == JNI_COMMIT) {
578 array->setRegion(start, size, elements);
579 }
580 }
581 };
582
583 // PinnedPrimitiveArray
584 // ///////////////////////////////////////////////////////////////////////////
585
586 template <typename T, typename Alloc>
587 PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) {
588 *this = std::move(o);
589 }
590
591 template <typename T, typename Alloc>
592 PinnedPrimitiveArray<T, Alloc>& PinnedPrimitiveArray<T, Alloc>::operator=(
593 PinnedPrimitiveArray&& o) {
594 if (array_) {
595 release();
596 }
597 array_ = std::move(o.array_);
598 elements_ = o.elements_;
599 isCopy_ = o.isCopy_;
600 size_ = o.size_;
601 start_ = o.start_;
602 o.clear();
603 return *this;
604 }
605
606 template <typename T, typename Alloc>
607 T* PinnedPrimitiveArray<T, Alloc>::get() {
608 return elements_;
609 }
610
611 template <typename T, typename Alloc>
612 inline void PinnedPrimitiveArray<T, Alloc>::release() {
613 releaseImpl(0);
614 clear();
615 }
616
617 template <typename T, typename Alloc>
618 inline void PinnedPrimitiveArray<T, Alloc>::commit() {
619 releaseImpl(JNI_COMMIT);
620 }
621
622 template <typename T, typename Alloc>
623 inline void PinnedPrimitiveArray<T, Alloc>::abort() {
624 releaseImpl(JNI_ABORT);
625 clear();
626 }
627
628 template <typename T, typename Alloc>
629 inline void PinnedPrimitiveArray<T, Alloc>::releaseImpl(jint mode) {
630 FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr);
631 Alloc::release(
632 array_,
633 elements_,
634 static_cast<jint>(start_),
635 static_cast<jint>(size_),
636 mode);
637 }
638
639 template <typename T, typename Alloc>
640 inline void PinnedPrimitiveArray<T, Alloc>::clear() noexcept {
641 array_ = nullptr;
642 elements_ = nullptr;
643 isCopy_ = false;
644 start_ = 0;
645 size_ = 0;
646 }
647
648 template <typename T, typename Alloc>
649 inline T& PinnedPrimitiveArray<T, Alloc>::operator[](size_t index) {
650 FACEBOOK_JNI_THROW_EXCEPTION_IF(elements_ == nullptr);
651 return elements_[index];
652 }
653
654 template <typename T, typename Alloc>
655 inline bool PinnedPrimitiveArray<T, Alloc>::isCopy() const noexcept {
656 return isCopy_ == JNI_TRUE;
657 }
658
659 template <typename T, typename Alloc>
660 inline size_t PinnedPrimitiveArray<T, Alloc>::size() const noexcept {
661 return size_;
662 }
663
664 template <typename T, typename Alloc>
665 inline PinnedPrimitiveArray<T, Alloc>::~PinnedPrimitiveArray() noexcept {
666 if (elements_) {
667 release();
668 }
669 }
670
671 template <typename T, typename Alloc>
672 inline PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(
673 alias_ref<typename jtype_traits<T>::array_type> array,
674 jint start,
675 jint length) {
676 array_ = array;
677 start_ = start;
678 Alloc::allocate(array, start, length, &elements_, &size_, &isCopy_);
679 }
680
681 template <typename T, typename Base, typename JType>
682 inline alias_ref<JClass> JavaClass<T, Base, JType>::javaClassStatic() {
683 static auto cls =
684 findClassStatic(jtype_traits<typename T::javaobject>::kBaseName.c_str());
685 return cls;
686 }
687
688 template <typename T, typename Base, typename JType>
689 inline local_ref<JClass> JavaClass<T, Base, JType>::javaClassLocal() {
690 std::string className(
691 jtype_traits<typename T::javaobject>::kBaseName.c_str());
692 return findClassLocal(className.c_str());
693 }
694
695 } // namespace jni
696 } // namespace facebook
697