1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_jni_Refs_h__ 8 #define mozilla_jni_Refs_h__ 9 10 #include <jni.h> 11 12 #include <utility> 13 14 #include "mozilla/fallible.h" 15 #include "mozilla/jni/Utils.h" 16 #include "mozilla/jni/TypeAdapter.h" 17 #include "nsError.h" // for nsresult 18 #include "nsString.h" 19 #include "nsTArray.h" 20 21 namespace mozilla { 22 namespace jni { 23 24 // Wrapped object reference (e.g. jobject, jclass, etc...) 25 template <class Cls, typename JNIType> 26 class Ref; 27 // Represents a calling context for JNI methods. 28 template <class Cls, typename JNIType> 29 class Context; 30 // Wrapped local reference that inherits from Ref. 31 template <class Cls> 32 class LocalRef; 33 // Wrapped global reference that inherits from Ref. 34 template <class Cls> 35 class GlobalRef; 36 // Wrapped weak reference that inherits from Ref. 37 template <class Cls> 38 class WeakRef; 39 // Wrapped dangling reference that's owned by someone else. 40 template <class Cls> 41 class DependentRef; 42 43 // Class to hold the native types of a method's arguments. 44 // For example, if a method has signature (ILjava/lang/String;)V, 45 // its arguments class would be jni::Args<int32_t, jni::String::Param> 46 template <typename...> 47 struct Args {}; 48 49 class Object; 50 51 // Base class for Ref and its specializations. 52 template <class Cls, typename Type> 53 class Ref { 54 template <class C, typename T> 55 friend class Ref; 56 57 using Self = Ref<Cls, Type>; 58 using bool_type = void (Self::*)() const; non_null_reference()59 void non_null_reference() const {} 60 61 // A Cls-derivative that allows copying 62 // (e.g. when acting as a return value). 63 struct CopyableCtx : public Context<Cls, Type> { CopyableCtxCopyableCtx64 CopyableCtx(JNIEnv* env, Type instance) 65 : Context<Cls, Type>(env, instance) {} 66 CopyableCtxCopyableCtx67 CopyableCtx(const CopyableCtx& cls) 68 : Context<Cls, Type>(cls.Env(), cls.Get()) {} 69 }; 70 71 // Private copy constructor so that there's no danger of assigning a 72 // temporary LocalRef/GlobalRef to a Ref, and potentially use the Ref 73 // after the source had been freed. 74 Ref(const Ref&) = default; 75 76 protected: FindEnv()77 static JNIEnv* FindEnv() { 78 return Cls::callingThread == CallingThread::GECKO ? GetGeckoThreadEnv() 79 : GetEnvForThread(); 80 } 81 82 Type mInstance; 83 84 // Protected jobject constructor because outside code should be using 85 // Ref::From. Using Ref::From makes it very easy to see which code is using 86 // raw JNI types for future refactoring. Ref(Type instance)87 explicit Ref(Type instance) : mInstance(instance) {} 88 89 public: 90 using JNIType = Type; 91 92 class AutoLock { 93 friend class Ref<Cls, Type>; 94 95 JNIEnv* const mEnv; 96 Type mInstance; 97 AutoLock(Type aInstance)98 explicit AutoLock(Type aInstance) 99 : mEnv(FindEnv()), mInstance(mEnv->NewLocalRef(aInstance)) { 100 mEnv->MonitorEnter(mInstance); 101 MOZ_CATCH_JNI_EXCEPTION(mEnv); 102 } 103 104 public: AutoLock(AutoLock && aOther)105 AutoLock(AutoLock&& aOther) 106 : mEnv(aOther.mEnv), mInstance(aOther.mInstance) { 107 aOther.mInstance = nullptr; 108 } 109 ~AutoLock()110 ~AutoLock() { Unlock(); } 111 Unlock()112 void Unlock() { 113 if (mInstance) { 114 mEnv->MonitorExit(mInstance); 115 mEnv->DeleteLocalRef(mInstance); 116 MOZ_CATCH_JNI_EXCEPTION(mEnv); 117 mInstance = nullptr; 118 } 119 } 120 }; 121 122 // Construct a Ref form a raw JNI reference. From(JNIType obj)123 static Ref<Cls, Type> From(JNIType obj) { return Ref<Cls, Type>(obj); } 124 125 // Construct a Ref form a generic object reference. From(const Ref<Object,jobject> & obj)126 static Ref<Cls, Type> From(const Ref<Object, jobject>& obj) { 127 return Ref<Cls, Type>(JNIType(obj.Get())); 128 } 129 Ref(decltype (nullptr))130 MOZ_IMPLICIT Ref(decltype(nullptr)) : mInstance(nullptr) {} 131 132 // Get the raw JNI reference. Get()133 JNIType Get() const { return mInstance; } 134 135 template <class T> IsInstanceOf()136 bool IsInstanceOf() const { 137 return FindEnv()->IsInstanceOf(mInstance, typename T::Context().ClassRef()); 138 } 139 140 template <class T> Cast()141 typename T::Ref Cast() const { 142 #ifdef MOZ_CHECK_JNI 143 MOZ_RELEASE_ASSERT(FindEnv()->IsAssignableFrom( 144 Context<Cls, Type>().ClassRef(), typename T::Context().ClassRef())); 145 #endif 146 return T::Ref::From(*this); 147 } 148 Lock()149 AutoLock Lock() const { return AutoLock(mInstance); } 150 151 bool operator==(const Ref& other) const { 152 // Treat two references of the same object as being the same. 153 return mInstance == other.mInstance || 154 JNI_FALSE != FindEnv()->IsSameObject(mInstance, other.mInstance); 155 } 156 157 bool operator!=(const Ref& other) const { return !operator==(other); } 158 159 bool operator==(decltype(nullptr)) const { return !mInstance; } 160 161 bool operator!=(decltype(nullptr)) const { return !!mInstance; } 162 163 CopyableCtx operator->() const { return CopyableCtx(FindEnv(), mInstance); } 164 165 CopyableCtx operator*() const { return operator->(); } 166 167 // Any ref can be cast to an object ref. 168 operator Ref<Object, jobject>() const { 169 return Ref<Object, jobject>(mInstance); 170 } 171 172 // Null checking (e.g. !!ref) using the safe-bool idiom. bool_type()173 operator bool_type() const { 174 return mInstance ? &Self::non_null_reference : nullptr; 175 } 176 177 // We don't allow implicit conversion to jobject because that can lead 178 // to easy mistakes such as assigning a temporary LocalRef to a jobject, 179 // and using the jobject after the LocalRef has been freed. 180 181 // We don't allow explicit conversion, to make outside code use Ref::Get. 182 // Using Ref::Get makes it very easy to see which code is using raw JNI 183 // types to make future refactoring easier. 184 185 // operator JNIType() const = delete; 186 }; 187 188 // Represents a calling context for JNI methods. 189 template <class Cls, typename Type> 190 class Context : public Ref<Cls, Type> { 191 using Ref = jni::Ref<Cls, Type>; 192 193 static jclass sClassRef; // global reference 194 195 protected: 196 JNIEnv* const mEnv; 197 198 public: Context()199 Context() : Ref(nullptr), mEnv(Ref::FindEnv()) {} 200 Context(JNIEnv * env,Type instance)201 Context(JNIEnv* env, Type instance) : Ref(instance), mEnv(env) {} 202 ClassRef()203 jclass ClassRef() const { 204 if (!sClassRef) { 205 const jclass cls = GetClassRef(mEnv, Cls::name); 206 sClassRef = jclass(mEnv->NewGlobalRef(cls)); 207 mEnv->DeleteLocalRef(cls); 208 } 209 return sClassRef; 210 } 211 Env()212 JNIEnv* Env() const { return mEnv; } 213 214 template <class T> IsInstanceOf()215 bool IsInstanceOf() const { 216 return mEnv->IsInstanceOf(Ref::mInstance, 217 typename T::Context(mEnv, nullptr).ClassRef()); 218 } 219 220 bool operator==(const Ref& other) const { 221 // Treat two references of the same object as being the same. 222 return Ref::mInstance == other.Get() || 223 JNI_FALSE != mEnv->IsSameObject(Ref::mInstance, other.Get()); 224 } 225 226 bool operator!=(const Ref& other) const { return !operator==(other); } 227 228 bool operator==(decltype(nullptr)) const { return !Ref::mInstance; } 229 230 bool operator!=(decltype(nullptr)) const { return !!Ref::mInstance; } 231 232 Cls operator->() const { 233 MOZ_ASSERT(Ref::mInstance, "Null jobject"); 234 return Cls(*this); 235 } 236 237 const Context<Cls, Type>& operator*() const { return *this; } 238 }; 239 240 template <class C, typename T> 241 jclass Context<C, T>::sClassRef; 242 243 template <class Cls, typename Type = jobject> 244 class ObjectBase { 245 protected: 246 const jni::Context<Cls, Type>& mCtx; 247 ClassRef()248 jclass ClassRef() const { return mCtx.ClassRef(); } Env()249 JNIEnv* Env() const { return mCtx.Env(); } Instance()250 Type Instance() const { return mCtx.Get(); } 251 252 public: 253 using Ref = jni::Ref<Cls, Type>; 254 using Context = jni::Context<Cls, Type>; 255 using LocalRef = jni::LocalRef<Cls>; 256 using GlobalRef = jni::GlobalRef<Cls>; 257 using WeakRef = jni::WeakRef<Cls>; 258 using Param = const Ref&; 259 260 static const CallingThread callingThread = CallingThread::ANY; 261 static const char name[]; 262 ObjectBase(const Context & ctx)263 explicit ObjectBase(const Context& ctx) : mCtx(ctx) {} 264 265 Cls* operator->() { return static_cast<Cls*>(this); } 266 }; 267 268 // Binding for a plain jobject. 269 class Object : public ObjectBase<Object, jobject> { 270 public: Object(const Context & ctx)271 explicit Object(const Context& ctx) : ObjectBase<Object, jobject>(ctx) {} 272 }; 273 274 // Binding for a built-in object reference other than jobject. 275 template <typename T> 276 class TypedObject : public ObjectBase<TypedObject<T>, T> { 277 public: TypedObject(const Context<TypedObject<T>,T> & ctx)278 explicit TypedObject(const Context<TypedObject<T>, T>& ctx) 279 : ObjectBase<TypedObject<T>, T>(ctx) {} 280 }; 281 282 // Binding for a boxed primitive object. 283 template <typename T> 284 class BoxedObject : public ObjectBase<BoxedObject<T>, jobject> { 285 public: BoxedObject(const Context<BoxedObject<T>,jobject> & ctx)286 explicit BoxedObject(const Context<BoxedObject<T>, jobject>& ctx) 287 : ObjectBase<BoxedObject<T>, jobject>(ctx) {} 288 }; 289 290 template <> 291 const char ObjectBase<Object, jobject>::name[]; 292 template <> 293 const char ObjectBase<TypedObject<jstring>, jstring>::name[]; 294 template <> 295 const char ObjectBase<TypedObject<jclass>, jclass>::name[]; 296 template <> 297 const char ObjectBase<TypedObject<jthrowable>, jthrowable>::name[]; 298 template <> 299 const char ObjectBase<BoxedObject<jboolean>, jobject>::name[]; 300 template <> 301 const char ObjectBase<BoxedObject<jbyte>, jobject>::name[]; 302 template <> 303 const char ObjectBase<BoxedObject<jchar>, jobject>::name[]; 304 template <> 305 const char ObjectBase<BoxedObject<jshort>, jobject>::name[]; 306 template <> 307 const char ObjectBase<BoxedObject<jint>, jobject>::name[]; 308 template <> 309 const char ObjectBase<BoxedObject<jlong>, jobject>::name[]; 310 template <> 311 const char ObjectBase<BoxedObject<jfloat>, jobject>::name[]; 312 template <> 313 const char ObjectBase<BoxedObject<jdouble>, jobject>::name[]; 314 template <> 315 const char ObjectBase<TypedObject<jbooleanArray>, jbooleanArray>::name[]; 316 template <> 317 const char ObjectBase<TypedObject<jbyteArray>, jbyteArray>::name[]; 318 template <> 319 const char ObjectBase<TypedObject<jcharArray>, jcharArray>::name[]; 320 template <> 321 const char ObjectBase<TypedObject<jshortArray>, jshortArray>::name[]; 322 template <> 323 const char ObjectBase<TypedObject<jintArray>, jintArray>::name[]; 324 template <> 325 const char ObjectBase<TypedObject<jlongArray>, jlongArray>::name[]; 326 template <> 327 const char ObjectBase<TypedObject<jfloatArray>, jfloatArray>::name[]; 328 template <> 329 const char ObjectBase<TypedObject<jdoubleArray>, jdoubleArray>::name[]; 330 template <> 331 const char ObjectBase<TypedObject<jobjectArray>, jobjectArray>::name[]; 332 333 // Define bindings for built-in types. 334 using String = TypedObject<jstring>; 335 using Class = TypedObject<jclass>; 336 using Throwable = TypedObject<jthrowable>; 337 338 using Boolean = BoxedObject<jboolean>; 339 using Byte = BoxedObject<jbyte>; 340 using Character = BoxedObject<jchar>; 341 using Short = BoxedObject<jshort>; 342 using Integer = BoxedObject<jint>; 343 using Long = BoxedObject<jlong>; 344 using Float = BoxedObject<jfloat>; 345 using Double = BoxedObject<jdouble>; 346 347 using BooleanArray = TypedObject<jbooleanArray>; 348 using ByteArray = TypedObject<jbyteArray>; 349 using CharArray = TypedObject<jcharArray>; 350 using ShortArray = TypedObject<jshortArray>; 351 using IntArray = TypedObject<jintArray>; 352 using LongArray = TypedObject<jlongArray>; 353 using FloatArray = TypedObject<jfloatArray>; 354 using DoubleArray = TypedObject<jdoubleArray>; 355 using ObjectArray = TypedObject<jobjectArray>; 356 357 namespace detail { 358 359 // See explanation in LocalRef. 360 template <class Cls> 361 struct GenericObject { 362 using Type = Object; 363 }; 364 template <> 365 struct GenericObject<Object> { 366 struct Type { 367 using Ref = jni::Ref<Type, jobject>; 368 using Context = jni::Context<Type, jobject>; 369 }; 370 }; 371 template <class Cls> 372 struct GenericLocalRef { 373 template <class C> 374 struct Type : jni::Object {}; 375 }; 376 template <> 377 struct GenericLocalRef<Object> { 378 template <class C> 379 using Type = jni::LocalRef<C>; 380 }; 381 382 } // namespace detail 383 384 template <class Cls> 385 class LocalRef : public Cls::Context { 386 template <class C> 387 friend class LocalRef; 388 389 using Ctx = typename Cls::Context; 390 using Ref = typename Cls::Ref; 391 using JNIType = typename Ref::JNIType; 392 393 // In order to be able to convert LocalRef<Object> to LocalRef<Cls>, we 394 // need constructors and copy assignment operators that take in a 395 // LocalRef<Object> argument. However, if Cls *is* Object, we would have 396 // duplicated constructors and operators with LocalRef<Object> arguments. To 397 // avoid this conflict, we use GenericObject, which is defined as Object for 398 // LocalRef<non-Object> and defined as a dummy class for LocalRef<Object>. 399 using GenericObject = typename detail::GenericObject<Cls>::Type; 400 401 // Similarly, GenericLocalRef is useed to convert LocalRef<Cls> to, 402 // LocalRef<Object>. It's defined as LocalRef<C> for Cls == Object, 403 // and defined as a dummy template class for Cls != Object. 404 template <class C> 405 using GenericLocalRef = 406 typename detail::GenericLocalRef<Cls>::template Type<C>; 407 408 static JNIType NewLocalRef(JNIEnv* env, JNIType obj) { 409 return JNIType(obj ? env->NewLocalRef(obj) : nullptr); 410 } 411 412 LocalRef(JNIEnv* env, JNIType instance) : Ctx(env, instance) {} 413 414 LocalRef& swap(LocalRef& other) { 415 auto instance = other.mInstance; 416 other.mInstance = Ctx::mInstance; 417 Ctx::mInstance = instance; 418 return *this; 419 } 420 421 public: 422 // Construct a LocalRef from a raw JNI local reference. Unlike Ref::From, 423 // LocalRef::Adopt returns a LocalRef that will delete the local reference 424 // when going out of scope. 425 static LocalRef Adopt(JNIType instance) { 426 return LocalRef(Ref::FindEnv(), instance); 427 } 428 429 static LocalRef Adopt(JNIEnv* env, JNIType instance) { 430 return LocalRef(env, instance); 431 } 432 433 // Copy constructor. 434 LocalRef(const LocalRef<Cls>& ref) 435 : Ctx(ref.mEnv, NewLocalRef(ref.mEnv, ref.mInstance)) {} 436 437 // Move constructor. 438 LocalRef(LocalRef<Cls>&& ref) : Ctx(ref.mEnv, ref.mInstance) { 439 ref.mInstance = nullptr; 440 } 441 442 explicit LocalRef(JNIEnv* env = Ref::FindEnv()) : Ctx(env, nullptr) {} 443 444 // Construct a LocalRef from any Ref, 445 // which means creating a new local reference. 446 MOZ_IMPLICIT LocalRef(const Ref& ref) : Ctx(Ref::FindEnv(), nullptr) { 447 Ctx::mInstance = NewLocalRef(Ctx::mEnv, ref.Get()); 448 } 449 450 LocalRef(JNIEnv* env, const Ref& ref) 451 : Ctx(env, NewLocalRef(env, ref.Get())) {} 452 453 // Move a LocalRef<Object> into a LocalRef<Cls> without 454 // creating/deleting local references. 455 MOZ_IMPLICIT LocalRef(LocalRef<GenericObject>&& ref) 456 : Ctx(ref.mEnv, JNIType(ref.mInstance)) { 457 ref.mInstance = nullptr; 458 } 459 460 template <class C> 461 MOZ_IMPLICIT LocalRef(GenericLocalRef<C>&& ref) 462 : Ctx(ref.mEnv, ref.mInstance) { 463 ref.mInstance = nullptr; 464 } 465 466 // Implicitly converts nullptr to LocalRef. 467 MOZ_IMPLICIT LocalRef(decltype(nullptr)) : Ctx(Ref::FindEnv(), nullptr) {} 468 469 ~LocalRef() { 470 if (Ctx::mInstance) { 471 Ctx::mEnv->DeleteLocalRef(Ctx::mInstance); 472 Ctx::mInstance = nullptr; 473 } 474 } 475 476 // Get the raw JNI reference that can be used as a return value. 477 // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref. 478 typename Ref::JNIType Forget() { 479 const auto obj = Ctx::Get(); 480 Ctx::mInstance = nullptr; 481 return obj; 482 } 483 484 LocalRef<Cls>& operator=(LocalRef<Cls> ref) & { return swap(ref); } 485 486 LocalRef<Cls>& operator=(const Ref& ref) & { 487 LocalRef<Cls> newRef(Ctx::mEnv, ref); 488 return swap(newRef); 489 } 490 491 LocalRef<Cls>& operator=(LocalRef<GenericObject>&& ref) & { 492 LocalRef<Cls> newRef(std::move(ref)); 493 return swap(newRef); 494 } 495 496 template <class C> 497 LocalRef<Cls>& operator=(GenericLocalRef<C>&& ref) & { 498 LocalRef<Cls> newRef(std::move(ref)); 499 return swap(newRef); 500 } 501 502 LocalRef<Cls>& operator=(decltype(nullptr)) & { 503 LocalRef<Cls> newRef(Ctx::mEnv, nullptr); 504 return swap(newRef); 505 } 506 }; 507 508 template <class Cls> 509 class GlobalRef : public Cls::Ref { 510 using Ref = typename Cls::Ref; 511 using JNIType = typename Ref::JNIType; 512 513 static JNIType NewGlobalRef(JNIEnv* env, JNIType instance) { 514 return JNIType(instance ? env->NewGlobalRef(instance) : nullptr); 515 } 516 517 GlobalRef& swap(GlobalRef& other) { 518 auto instance = other.mInstance; 519 other.mInstance = Ref::mInstance; 520 Ref::mInstance = instance; 521 return *this; 522 } 523 524 public: 525 GlobalRef() : Ref(nullptr) {} 526 527 // Copy constructor 528 GlobalRef(const GlobalRef& ref) 529 : Ref(NewGlobalRef(GetEnvForThread(), ref.mInstance)) {} 530 531 // Move constructor 532 GlobalRef(GlobalRef&& ref) : Ref(ref.mInstance) { ref.mInstance = nullptr; } 533 534 MOZ_IMPLICIT GlobalRef(const Ref& ref) 535 : Ref(NewGlobalRef(GetEnvForThread(), ref.Get())) {} 536 537 GlobalRef(JNIEnv* env, const Ref& ref) : Ref(NewGlobalRef(env, ref.Get())) {} 538 539 MOZ_IMPLICIT GlobalRef(const LocalRef<Cls>& ref) 540 : Ref(NewGlobalRef(ref.Env(), ref.Get())) {} 541 542 // Implicitly converts nullptr to GlobalRef. 543 MOZ_IMPLICIT GlobalRef(decltype(nullptr)) : Ref(nullptr) {} 544 545 ~GlobalRef() { 546 if (Ref::mInstance) { 547 Clear(GetEnvForThread()); 548 } 549 } 550 551 // Get the raw JNI reference that can be used as a return value. 552 // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref. 553 typename Ref::JNIType Forget() { 554 const auto obj = Ref::Get(); 555 Ref::mInstance = nullptr; 556 return obj; 557 } 558 559 void Clear(JNIEnv* env) { 560 if (Ref::mInstance) { 561 env->DeleteGlobalRef(Ref::mInstance); 562 Ref::mInstance = nullptr; 563 } 564 } 565 566 GlobalRef<Cls>& operator=(GlobalRef<Cls> ref) & { return swap(ref); } 567 568 GlobalRef<Cls>& operator=(const Ref& ref) & { 569 GlobalRef<Cls> newRef(ref); 570 return swap(newRef); 571 } 572 573 GlobalRef<Cls>& operator=(const LocalRef<Cls>& ref) & { 574 GlobalRef<Cls> newRef(ref); 575 return swap(newRef); 576 } 577 578 GlobalRef<Cls>& operator=(decltype(nullptr)) & { 579 GlobalRef<Cls> newRef(nullptr); 580 return swap(newRef); 581 } 582 }; 583 584 template <class Cls> 585 class WeakRef : public Ref<Cls, jweak> { 586 using Ref = Ref<Cls, jweak>; 587 using JNIType = typename Ref::JNIType; 588 589 static JNIType NewWeakRef(JNIEnv* env, JNIType instance) { 590 return JNIType(instance ? env->NewWeakGlobalRef(instance) : nullptr); 591 } 592 593 WeakRef& swap(WeakRef& other) { 594 auto instance = other.mInstance; 595 other.mInstance = Ref::mInstance; 596 Ref::mInstance = instance; 597 return *this; 598 } 599 600 public: 601 WeakRef() : Ref(nullptr) {} 602 603 // Copy constructor 604 WeakRef(const WeakRef& ref) 605 : Ref(NewWeakRef(GetEnvForThread(), ref.mInstance)) {} 606 607 // Move constructor 608 WeakRef(WeakRef&& ref) : Ref(ref.mInstance) { ref.mInstance = nullptr; } 609 610 MOZ_IMPLICIT WeakRef(const Ref& ref) 611 : Ref(NewWeakRef(GetEnvForThread(), ref.Get())) {} 612 613 WeakRef(JNIEnv* env, const Ref& ref) : Ref(NewWeakRef(env, ref.Get())) {} 614 615 MOZ_IMPLICIT WeakRef(const LocalRef<Cls>& ref) 616 : Ref(NewWeakRef(ref.Env(), ref.Get())) {} 617 618 // Implicitly converts nullptr to WeakRef. 619 MOZ_IMPLICIT WeakRef(decltype(nullptr)) : Ref(nullptr) {} 620 621 ~WeakRef() { 622 if (Ref::mInstance) { 623 Clear(GetEnvForThread()); 624 } 625 } 626 627 // Get the raw JNI reference that can be used as a return value. 628 // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref. 629 typename Ref::JNIType Forget() { 630 const auto obj = Ref::Get(); 631 Ref::mInstance = nullptr; 632 return obj; 633 } 634 635 void Clear(JNIEnv* env) { 636 if (Ref::mInstance) { 637 env->DeleteWeakGlobalRef(Ref::mInstance); 638 Ref::mInstance = nullptr; 639 } 640 } 641 642 WeakRef<Cls>& operator=(WeakRef<Cls> ref) & { return swap(ref); } 643 644 WeakRef<Cls>& operator=(const Ref& ref) & { 645 WeakRef<Cls> newRef(ref); 646 return swap(newRef); 647 } 648 649 WeakRef<Cls>& operator=(const LocalRef<Cls>& ref) & { 650 WeakRef<Cls> newRef(ref); 651 return swap(newRef); 652 } 653 654 WeakRef<Cls>& operator=(decltype(nullptr)) & { 655 WeakRef<Cls> newRef(nullptr); 656 return swap(newRef); 657 } 658 659 void operator->() const = delete; 660 void operator*() const = delete; 661 }; 662 663 template <class Cls> 664 class DependentRef : public Cls::Ref { 665 using Ref = typename Cls::Ref; 666 667 public: 668 explicit DependentRef(typename Ref::JNIType instance) : Ref(instance) {} 669 670 DependentRef(const DependentRef& ref) : Ref(ref.Get()) {} 671 }; 672 673 class StringParam; 674 675 template <> 676 class TypedObject<jstring> : public ObjectBase<TypedObject<jstring>, jstring> { 677 using Base = ObjectBase<TypedObject<jstring>, jstring>; 678 679 public: 680 using Param = const StringParam&; 681 682 explicit TypedObject(const Context& ctx) : Base(ctx) {} 683 684 size_t Length() const { 685 const size_t ret = Base::Env()->GetStringLength(Base::Instance()); 686 MOZ_CATCH_JNI_EXCEPTION(Base::Env()); 687 return ret; 688 } 689 690 nsString ToString() const { 691 const jchar* const str = 692 Base::Env()->GetStringChars(Base::Instance(), nullptr); 693 const jsize len = Base::Env()->GetStringLength(Base::Instance()); 694 695 nsString result(reinterpret_cast<const char16_t*>(str), len); 696 Base::Env()->ReleaseStringChars(Base::Instance(), str); 697 return result; 698 } 699 700 nsCString ToCString() const { return NS_ConvertUTF16toUTF8(ToString()); } 701 702 // Convert jstring to a nsString. 703 operator nsString() const { return ToString(); } 704 705 // Convert jstring to a nsCString. 706 operator nsCString() const { return ToCString(); } 707 }; 708 709 // Define a custom parameter type for String, 710 // which accepts both String::Ref and nsAString/nsACString 711 class StringParam : public String::Ref { 712 using Ref = String::Ref; 713 714 private: 715 // Not null if we should delete ref on destruction. 716 JNIEnv* const mEnv; 717 718 static jstring GetString(JNIEnv* env, const nsAString& str) { 719 const jstring result = env->NewString( 720 reinterpret_cast<const jchar*>(str.BeginReading()), str.Length()); 721 if (!result) { 722 NS_ABORT_OOM(str.Length() * sizeof(char16_t)); 723 } 724 MOZ_CATCH_JNI_EXCEPTION(env); 725 return result; 726 } 727 728 static jstring GetString(JNIEnv* env, const nsAString& str, 729 const fallible_t&) { 730 const jstring result = env->NewString( 731 reinterpret_cast<const jchar*>(str.BeginReading()), str.Length()); 732 if (env->ExceptionCheck()) { 733 #ifdef MOZ_CHECK_JNI 734 env->ExceptionDescribe(); 735 #endif 736 env->ExceptionClear(); 737 } 738 return result; 739 } 740 741 static jstring GetString(JNIEnv* env, const nsACString& str, 742 const fallible_t& aFallible) { 743 nsAutoString utf16; 744 if (!CopyUTF8toUTF16(str, utf16, aFallible)) { 745 return nullptr; 746 } 747 return GetString(env, utf16, aFallible); 748 } 749 750 public: 751 MOZ_IMPLICIT StringParam(decltype(nullptr)) : Ref(nullptr), mEnv(nullptr) {} 752 753 MOZ_IMPLICIT StringParam(const Ref& ref) : Ref(ref.Get()), mEnv(nullptr) {} 754 755 MOZ_IMPLICIT StringParam(const nsAString& str, JNIEnv* env = Ref::FindEnv()) 756 : Ref(GetString(env, str)), mEnv(env) {} 757 758 MOZ_IMPLICIT StringParam(const nsAString& str, JNIEnv* env, 759 const fallible_t& aFallible) 760 : Ref(GetString(env, str, aFallible)), mEnv(env) {} 761 762 MOZ_IMPLICIT StringParam(const nsLiteralString& str, 763 JNIEnv* env = Ref::FindEnv()) 764 : Ref(GetString(env, str)), mEnv(env) {} 765 766 MOZ_IMPLICIT StringParam(const char16_t* str, JNIEnv* env = Ref::FindEnv()) 767 : Ref(GetString(env, nsDependentString(str))), mEnv(env) {} 768 769 MOZ_IMPLICIT StringParam(const nsACString& str, JNIEnv* env = Ref::FindEnv()) 770 : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {} 771 772 MOZ_IMPLICIT StringParam(const nsACString& str, JNIEnv* env, 773 const fallible_t& aFallible) 774 : Ref(GetString(env, str, aFallible)), mEnv(env) {} 775 776 MOZ_IMPLICIT StringParam(const nsLiteralCString& str, 777 JNIEnv* env = Ref::FindEnv()) 778 : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {} 779 780 MOZ_IMPLICIT StringParam(const char* str, JNIEnv* env = Ref::FindEnv()) 781 : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {} 782 783 StringParam(StringParam&& other) : Ref(other.Get()), mEnv(other.mEnv) { 784 other.mInstance = nullptr; 785 } 786 787 ~StringParam() { 788 if (mEnv && Get()) { 789 mEnv->DeleteLocalRef(Get()); 790 } 791 } 792 793 operator String::LocalRef() const { 794 // We can't return our existing ref because the returned 795 // LocalRef could be freed first, so we need a new local ref. 796 return String::LocalRef(mEnv ? mEnv : Ref::FindEnv(), *this); 797 } 798 }; 799 800 namespace detail { 801 template <typename T> 802 struct TypeAdapter; 803 } 804 805 // Ref specialization for arrays. 806 template <typename JNIType, class ElementType> 807 class ArrayRefBase : public ObjectBase<TypedObject<JNIType>, JNIType> { 808 protected: 809 using Base = ObjectBase<TypedObject<JNIType>, JNIType>; 810 811 public: 812 explicit ArrayRefBase(const Context<TypedObject<JNIType>, JNIType>& ctx) 813 : Base(ctx) {} 814 815 static typename Base::LocalRef New(const ElementType* data, size_t length) { 816 using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType; 817 static_assert(sizeof(ElementType) == sizeof(JNIElemType), 818 "Size of native type must match size of JNI type"); 819 JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); 820 auto result = (jenv->*detail::TypeAdapter<ElementType>::NewArray)(length); 821 MOZ_CATCH_JNI_EXCEPTION(jenv); 822 (jenv->*detail::TypeAdapter<ElementType>::SetArray)( 823 result, jsize(0), length, reinterpret_cast<const JNIElemType*>(data)); 824 MOZ_CATCH_JNI_EXCEPTION(jenv); 825 return Base::LocalRef::Adopt(jenv, result); 826 } 827 828 size_t Length() const { 829 const size_t ret = Base::Env()->GetArrayLength(Base::Instance()); 830 MOZ_CATCH_JNI_EXCEPTION(Base::Env()); 831 return ret; 832 } 833 834 ElementType GetElement(size_t index) const { 835 using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType; 836 static_assert(sizeof(ElementType) == sizeof(JNIElemType), 837 "Size of native type must match size of JNI type"); 838 839 ElementType ret; 840 (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)( 841 Base::Instance(), jsize(index), 1, 842 reinterpret_cast<JNIElemType*>(&ret)); 843 MOZ_CATCH_JNI_EXCEPTION(Base::Env()); 844 return ret; 845 } 846 847 nsTArray<ElementType> GetElements() const { 848 using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType; 849 static_assert(sizeof(ElementType) == sizeof(JNIElemType), 850 "Size of native type must match size of JNI type"); 851 852 const size_t len = size_t(Base::Env()->GetArrayLength(Base::Instance())); 853 854 nsTArray<ElementType> array(len); 855 array.SetLength(len); 856 CopyTo(array.Elements(), len); 857 return array; 858 } 859 860 // returns number of elements copied 861 size_t CopyTo(ElementType* buffer, size_t size) const { 862 using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType; 863 static_assert(sizeof(ElementType) == sizeof(JNIElemType), 864 "Size of native type must match size of JNI type"); 865 866 const size_t len = size_t(Base::Env()->GetArrayLength(Base::Instance())); 867 const size_t amountToCopy = (len > size ? size : len); 868 (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)( 869 Base::Instance(), 0, jsize(amountToCopy), 870 reinterpret_cast<JNIElemType*>(buffer)); 871 return amountToCopy; 872 } 873 874 ElementType operator[](size_t index) const { return GetElement(index); } 875 876 operator nsTArray<ElementType>() const { return GetElements(); } 877 }; 878 879 #define DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \ 880 template <> \ 881 class TypedObject<JNIType> : public ArrayRefBase<JNIType, ElementType> { \ 882 public: \ 883 explicit TypedObject(const Context& ctx) \ 884 : ArrayRefBase<JNIType, ElementType>(ctx) {} \ 885 static typename Base::LocalRef From(const nsTArray<ElementType>& aArray) { \ 886 return New(aArray.Elements(), aArray.Length()); \ 887 } 888 889 #define DEFINE_PRIMITIVE_ARRAY_REF_FOOTER } 890 891 #define DEFINE_PRIMITIVE_ARRAY_REF_FROM_IMPLICIT_CONVERSION(ElementType, \ 892 ConvertFromType) \ 893 static typename Base::LocalRef From( \ 894 const nsTArray<ConvertFromType>& aArray) { \ 895 return New(reinterpret_cast<const ElementType*>(aArray.Elements()), \ 896 aArray.Length()); \ 897 } 898 899 #define DEFINE_PRIMITIVE_ARRAY_REF(JNIType, ElementType) \ 900 DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \ 901 DEFINE_PRIMITIVE_ARRAY_REF_FOOTER 902 903 #define DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION( \ 904 JNIType, ElementType, ConvertFromType) \ 905 DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \ 906 DEFINE_PRIMITIVE_ARRAY_REF_FROM_IMPLICIT_CONVERSION(ElementType, \ 907 ConvertFromType) \ 908 DEFINE_PRIMITIVE_ARRAY_REF_FOOTER 909 910 DEFINE_PRIMITIVE_ARRAY_REF(jbooleanArray, bool); 911 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jbyteArray, int8_t, 912 uint8_t); 913 DEFINE_PRIMITIVE_ARRAY_REF(jcharArray, char16_t); 914 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jshortArray, int16_t, 915 uint16_t); 916 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jintArray, int32_t, 917 uint32_t); 918 DEFINE_PRIMITIVE_ARRAY_REF(jfloatArray, float); 919 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jlongArray, int64_t, 920 uint64_t); 921 DEFINE_PRIMITIVE_ARRAY_REF(jdoubleArray, double); 922 923 #undef DEFINE_PRIMITIVE_ARRAY_REF 924 #undef DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION 925 #undef DEFINE_PRIMITIVE_ARRAY_HEADER 926 #undef DEFINE_PRIMITIVE_ARRAY_FROM_IMPLICIT_CONVERSION 927 #undef DEFINE_PRIMITIVE_ARRAY_FOOTER 928 929 class ByteBuffer : public ObjectBase<ByteBuffer, jobject> { 930 public: 931 explicit ByteBuffer(const Context& ctx) 932 : ObjectBase<ByteBuffer, jobject>(ctx) {} 933 934 static LocalRef New(void* data, size_t capacity) { 935 JNIEnv* const env = GetEnvForThread(); 936 const auto ret = 937 LocalRef::Adopt(env, env->NewDirectByteBuffer(data, jlong(capacity))); 938 MOZ_CATCH_JNI_EXCEPTION(env); 939 return ret; 940 } 941 942 void* Address() { 943 void* const ret = Env()->GetDirectBufferAddress(Instance()); 944 MOZ_CATCH_JNI_EXCEPTION(Env()); 945 return ret; 946 } 947 948 size_t Capacity() { 949 const size_t ret = size_t(Env()->GetDirectBufferCapacity(Instance())); 950 MOZ_CATCH_JNI_EXCEPTION(Env()); 951 return ret; 952 } 953 }; 954 955 template <> 956 const char ObjectBase<ByteBuffer, jobject>::name[]; 957 958 template <> 959 class TypedObject<jobjectArray> 960 : public ObjectBase<TypedObject<jobjectArray>, jobjectArray> { 961 using Base = ObjectBase<TypedObject<jobjectArray>, jobjectArray>; 962 963 public: 964 template <class Cls = Object> 965 static Base::LocalRef New(size_t length, 966 typename Cls::Param initialElement = nullptr) { 967 JNIEnv* const env = GetEnvForThread(); 968 jobjectArray array = env->NewObjectArray( 969 jsize(length), typename Cls::Context(env, nullptr).ClassRef(), 970 initialElement.Get()); 971 MOZ_CATCH_JNI_EXCEPTION(env); 972 return Base::LocalRef::Adopt(env, array); 973 } 974 975 explicit TypedObject(const Context& ctx) : Base(ctx) {} 976 977 size_t Length() const { 978 const size_t ret = Base::Env()->GetArrayLength(Base::Instance()); 979 MOZ_CATCH_JNI_EXCEPTION(Base::Env()); 980 return ret; 981 } 982 983 Object::LocalRef GetElement(size_t index) const { 984 auto ret = Object::LocalRef::Adopt( 985 Base::Env(), 986 Base::Env()->GetObjectArrayElement(Base::Instance(), jsize(index))); 987 MOZ_CATCH_JNI_EXCEPTION(Base::Env()); 988 return ret; 989 } 990 991 nsTArray<Object::LocalRef> GetElements() const { 992 const jsize len = size_t(Base::Env()->GetArrayLength(Base::Instance())); 993 994 nsTArray<Object::LocalRef> array((size_t(len))); 995 for (jsize i = 0; i < len; i++) { 996 array.AppendElement(Object::LocalRef::Adopt( 997 Base::Env(), 998 Base::Env()->GetObjectArrayElement(Base::Instance(), i))); 999 MOZ_CATCH_JNI_EXCEPTION(Base::Env()); 1000 } 1001 return array; 1002 } 1003 1004 Object::LocalRef operator[](size_t index) const { return GetElement(index); } 1005 1006 operator nsTArray<Object::LocalRef>() const { return GetElements(); } 1007 1008 void SetElement(size_t index, Object::Param element) const { 1009 Base::Env()->SetObjectArrayElement(Base::Instance(), jsize(index), 1010 element.Get()); 1011 MOZ_CATCH_JNI_EXCEPTION(Base::Env()); 1012 } 1013 }; 1014 1015 // Support conversion from LocalRef<T>* to LocalRef<Object>*: 1016 // LocalRef<Foo> foo; 1017 // Foo::GetFoo(&foo); // error because parameter type is LocalRef<Object>*. 1018 // Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument. 1019 template <class Cls> 1020 class ReturnToLocal { 1021 private: 1022 LocalRef<Cls>* const localRef; 1023 LocalRef<Object> objRef; 1024 1025 public: 1026 explicit ReturnToLocal(LocalRef<Cls>* ref) : localRef(ref) {} 1027 operator LocalRef<Object>*() { return &objRef; } 1028 1029 ~ReturnToLocal() { 1030 if (objRef) { 1031 *localRef = std::move(objRef); 1032 } 1033 } 1034 }; 1035 1036 template <class Cls> 1037 ReturnToLocal<Cls> ReturnTo(LocalRef<Cls>* ref) { 1038 return ReturnToLocal<Cls>(ref); 1039 } 1040 1041 // Support conversion from GlobalRef<T>* to LocalRef<Object/T>*: 1042 // GlobalRef<Foo> foo; 1043 // Foo::GetFoo(&foo); // error because parameter type is LocalRef<Foo>*. 1044 // Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument. 1045 template <class Cls> 1046 class ReturnToGlobal { 1047 private: 1048 GlobalRef<Cls>* const globalRef; 1049 LocalRef<Object> objRef; 1050 LocalRef<Cls> clsRef; 1051 1052 public: 1053 explicit ReturnToGlobal(GlobalRef<Cls>* ref) : globalRef(ref) {} 1054 operator LocalRef<Object>*() { return &objRef; } 1055 operator LocalRef<Cls>*() { return &clsRef; } 1056 1057 ~ReturnToGlobal() { 1058 if (objRef) { 1059 *globalRef = (clsRef = std::move(objRef)); 1060 } else if (clsRef) { 1061 *globalRef = clsRef; 1062 } 1063 } 1064 }; 1065 1066 template <class Cls> 1067 ReturnToGlobal<Cls> ReturnTo(GlobalRef<Cls>* ref) { 1068 return ReturnToGlobal<Cls>(ref); 1069 } 1070 1071 // Make a LocalRef<T> from any other Ref<T> 1072 template <typename Cls, typename JNIType> 1073 LocalRef<Cls> ToLocalRef(const Ref<Cls, JNIType>& aRef) { 1074 return LocalRef<Cls>(aRef); 1075 } 1076 1077 } // namespace jni 1078 } // namespace mozilla 1079 1080 #endif // mozilla_jni_Refs_h__ 1081