The open source OpenXR runtime
1#ifdef _WIN32
2# define WIN32_LEAN_AND_MEAN 1
3
4 // Windows Dependencies
5# include <windows.h>
6#else
7 // UNIX Dependencies
8# include <dlfcn.h>
9#endif
10
11// External Dependencies
12#include <jni.h>
13
14// Standard Dependencies
15#include <atomic>
16#include <string>
17
18// Local Dependencies
19#include "jnipp.h"
20
21namespace jni
22{
23 // Static Variables
24 static std::atomic_bool isVm(false);
25 static JavaVM* javaVm = nullptr;
26
27 /**
28 Maintains the lifecycle of a JNIEnv.
29 */
30 class ScopedEnv final
31 {
32 public:
33 ScopedEnv() noexcept : _vm(nullptr), _env(nullptr), _attached(false) {}
34 ~ScopedEnv();
35
36 void init(JavaVM* vm);
37 JNIEnv* get() const noexcept { return _env; }
38
39 private:
40 // Instance Variables
41 JavaVM* _vm;
42 JNIEnv* _env;
43 bool _attached; ///< Manually attached, as opposed to already attached.
44 };
45
46 ScopedEnv::~ScopedEnv()
47 {
48 if (_vm && _attached)
49 _vm->DetachCurrentThread();
50 }
51
52 void ScopedEnv::init(JavaVM* vm)
53 {
54 if (_env != nullptr)
55 return;
56
57 if (vm == nullptr)
58 throw InitializationException("JNI not initialized");
59
60 if (vm->GetEnv((void**)&_env, JNI_VERSION_1_2) != JNI_OK)
61 {
62#ifdef __ANDROID__
63 if (vm->AttachCurrentThread(&_env, nullptr) != 0)
64#else
65 if (vm->AttachCurrentThread((void**)&_env, nullptr) != 0)
66#endif
67 throw InitializationException("Could not attach JNI to thread");
68
69 _attached = true;
70 }
71
72 _vm = vm;
73 }
74
75 /*
76 Helper Functions
77 */
78
79#ifdef _WIN32
80
81 static bool fileExists(const std::string& filePath)
82 {
83 DWORD attr = ::GetFileAttributesA(filePath.c_str());
84
85 return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
86 }
87
88#else
89
90 /**
91 Convert from a UTF-16 Java string to a UTF-32 string.
92 */
93 std::wstring toWString(const jchar* str, jsize length)
94 {
95 std::wstring result;
96
97 result.reserve(length);
98
99 for (jsize i = 0; i < length; ++i)
100 {
101 wchar_t ch = str[i];
102
103 // Check for a two-segment character.
104 if (ch >= wchar_t(0xD800) && ch <= wchar_t(0xDBFF)) {
105 if (i + 1 >= length)
106 break;
107
108 // Create a single, 32-bit character.
109 ch = (ch - wchar_t(0xD800)) << 10;
110 ch += str[i++] - wchar_t(0x1DC00);
111 }
112
113 result += ch;
114 }
115
116 return result;
117 }
118
119 /**
120 Convert from a UTF-32 string to a UTF-16 Java string.
121 */
122 std::basic_string<jchar> toJString(const wchar_t* str, size_t length)
123 {
124 std::basic_string<jchar> result;
125
126 result.reserve(length * 2); // Worst case scenario.
127
128 for (size_t i = 0; i < length; ++i)
129 {
130 wchar_t ch = str[i];
131
132 // Check for multi-byte UTF-16 character.
133 if (ch > wchar_t(0xFFFF)) {
134 ch -= uint32_t(0x10000);
135
136 // Add the first of the two-segment character.
137 result += jchar(0xD800 + (ch >> 10));
138 ch = wchar_t(0xDC00) + (ch & 0x03FF);
139 }
140
141 result += jchar(ch);
142 }
143
144 return result;
145 }
146
147#endif // _WIN32
148
149 JNIEnv* env()
150 {
151 static thread_local ScopedEnv env;
152
153 if (env.get() == nullptr)
154 env.init(javaVm);
155
156 return env.get();
157 }
158
159 static jclass findClass(const char* name)
160 {
161 jclass ref = env()->FindClass(name);
162
163 if (ref == nullptr)
164 {
165 env()->ExceptionClear();
166 throw NameResolutionException(name);
167 }
168
169 return ref;
170 }
171
172 static void handleJavaExceptions()
173 {
174 JNIEnv* env = jni::env();
175
176 jthrowable exception = env->ExceptionOccurred();
177
178 if (exception != nullptr)
179 {
180 Object obj(exception, Object::Temporary);
181
182 env->ExceptionClear();
183 std::string msg = obj.call<std::string>("toString");
184 throw InvocationException(msg.c_str());
185 }
186 }
187
188 static std::string toString(jobject handle, bool deleteLocal = true)
189 {
190 std::string result;
191
192 if (handle != nullptr)
193 {
194 JNIEnv* env = jni::env();
195
196 const char* chars = env->GetStringUTFChars(jstring(handle), nullptr);
197 result.assign(chars, env->GetStringUTFLength(jstring(handle)));
198 env->ReleaseStringUTFChars(jstring(handle), chars);
199
200 if (deleteLocal)
201 env->DeleteLocalRef(handle);
202 }
203
204 return result;
205 }
206
207 static std::wstring toWString(jobject handle, bool deleteLocal = true)
208 {
209 std::wstring result;
210
211 if (handle != nullptr)
212 {
213 JNIEnv* env = jni::env();
214
215 const jchar* chars = env->GetStringChars(jstring(handle), nullptr);
216#ifdef _WIN32
217 result.assign((const wchar_t*) chars, env->GetStringLength(jstring(handle)));
218#else
219 result = toWString(chars, env->GetStringLength(jstring(handle)));
220#endif
221 env->ReleaseStringChars(jstring(handle), chars);
222
223 if (deleteLocal)
224 env->DeleteLocalRef(handle);
225 }
226
227 return result;
228 }
229
230
231 /*
232 Stand-alone Function Implementations
233 */
234
235 void init(JNIEnv* env)
236 {
237 bool expected = false;
238
239 if (isVm.compare_exchange_strong(expected, true))
240 {
241 if (javaVm == nullptr && env->GetJavaVM(&javaVm) != 0)
242 throw InitializationException("Could not acquire Java VM");
243 }
244 }
245
246 void init(JavaVM* vm) {
247 bool expected = false;
248
249 if (isVm.compare_exchange_strong(expected, true))
250 {
251 javaVm = vm;
252 }
253 }
254 /*
255 Object Implementation
256 */
257
258 Object::Object() noexcept : _handle(nullptr), _class(nullptr), _isGlobal(false)
259 {
260 }
261
262 Object::Object(const Object& other) : _handle(nullptr), _class(nullptr), _isGlobal(!other.isNull())
263 {
264 if (!other.isNull())
265 _handle = env()->NewGlobalRef(other._handle);
266 }
267
268 Object::Object(Object&& other) noexcept : _handle(other._handle), _class(other._class), _isGlobal(other._isGlobal)
269 {
270 other._handle = nullptr;
271 other._class = nullptr;
272 other._isGlobal = false;
273 }
274
275 Object::Object(jobject ref, int scopeFlags) : _handle(ref), _class(nullptr), _isGlobal((scopeFlags & Temporary) == 0)
276 {
277 if (!_isGlobal)
278 return;
279
280 JNIEnv* env = jni::env();
281
282 _handle = env->NewGlobalRef(ref);
283
284 if (scopeFlags & DeleteLocalInput)
285 env->DeleteLocalRef(ref);
286 }
287
288 Object::~Object() noexcept
289 {
290 JNIEnv* env = jni::env();
291
292 if (_isGlobal)
293 env->DeleteGlobalRef(_handle);
294
295 if (_class != nullptr)
296 env->DeleteGlobalRef(_class);
297 }
298
299 Object& Object::operator=(const Object& other)
300 {
301 if (_handle != other._handle)
302 {
303 JNIEnv* env = jni::env();
304
305 // Ditch the old reference.
306 if (_isGlobal)
307 env->DeleteGlobalRef(_handle);
308 if (_class != nullptr)
309 env->DeleteGlobalRef(_class);
310
311 // Assign the new reference.
312 if ((_isGlobal = !other.isNull()) != false)
313 _handle = env->NewGlobalRef(other._handle);
314
315 _class = nullptr;
316 }
317
318 return *this;
319 }
320
321 bool Object::operator==(const Object& other) const
322 {
323 return env()->IsSameObject(_handle, other._handle) != JNI_FALSE;
324 }
325
326 Object& Object::operator=(Object&& other)
327 {
328 if (_handle != other._handle)
329 {
330 JNIEnv* env = jni::env();
331
332 // Ditch the old reference.
333 if (_isGlobal)
334 env->DeleteGlobalRef(_handle);
335 if (_class != nullptr)
336 env->DeleteGlobalRef(_class);
337
338 // Assign the new reference.
339 _handle = other._handle;
340 _isGlobal = other._isGlobal;
341 _class = other._class;
342
343 other._handle = nullptr;
344 other._isGlobal = false;
345 other._class = nullptr;
346 }
347
348 return *this;
349 }
350
351 bool Object::isNull() const noexcept
352 {
353 return _handle == nullptr || env()->IsSameObject(_handle, nullptr);
354 }
355
356 template <> void Object::callMethod(method_t method, internal::value_t* args) const
357 {
358 env()->CallVoidMethodA(_handle, method, (jvalue*) args);
359 handleJavaExceptions();
360 }
361
362 template <> bool Object::callMethod(method_t method, internal::value_t* args) const
363 {
364 auto result = env()->CallBooleanMethodA(_handle, method, (jvalue*) args);
365 handleJavaExceptions();
366 return result != 0;
367 }
368
369 template <> bool Object::get(field_t field) const
370 {
371 return env()->GetBooleanField(_handle, field) != 0;
372 }
373
374 template <> void Object::set(field_t field, const bool& value)
375 {
376 env()->SetBooleanField(_handle, field, value);
377 }
378
379 template <> byte_t Object::callMethod(method_t method, internal::value_t* args) const
380 {
381 auto result = env()->CallByteMethodA(_handle, method, (jvalue*) args);
382 handleJavaExceptions();
383 return result;
384 }
385
386 template <> wchar_t Object::callMethod(method_t method, internal::value_t* args) const
387 {
388 auto result = env()->CallCharMethodA(_handle, method, (jvalue*) args);
389 handleJavaExceptions();
390 return result;
391 }
392
393 template <> short Object::callMethod(method_t method, internal::value_t* args) const
394 {
395 auto result = env()->CallShortMethodA(_handle, method, (jvalue*) args);
396 handleJavaExceptions();
397 return result;
398 }
399
400 template <> int Object::callMethod(method_t method, internal::value_t* args) const
401 {
402 auto result = env()->CallIntMethodA(_handle, method, (jvalue*) args);
403 handleJavaExceptions();
404 return result;
405 }
406
407 template <> long long Object::callMethod(method_t method, internal::value_t* args) const
408 {
409 auto result = env()->CallLongMethodA(_handle, method, (jvalue*) args);
410 handleJavaExceptions();
411 return result;
412 }
413
414 template <> float Object::callMethod(method_t method, internal::value_t* args) const
415 {
416 auto result = env()->CallFloatMethodA(_handle, method, (jvalue*) args);
417 handleJavaExceptions();
418 return result;
419 }
420
421 template <> double Object::callMethod(method_t method, internal::value_t* args) const
422 {
423 auto result = env()->CallDoubleMethodA(_handle, method, (jvalue*) args);
424 handleJavaExceptions();
425 return result;
426 }
427
428 template <> std::string Object::callMethod(method_t method, internal::value_t* args) const
429 {
430 auto result = env()->CallObjectMethodA(_handle, method, (jvalue*) args);
431 handleJavaExceptions();
432 return toString(result);
433 }
434
435 template <> std::wstring Object::callMethod(method_t method, internal::value_t* args) const
436 {
437 auto result = env()->CallObjectMethodA(_handle, method, (jvalue*) args);
438 handleJavaExceptions();
439 return toWString(result);
440 }
441
442 template <> jni::Object Object::callMethod(method_t method, internal::value_t* args) const
443 {
444 auto result = env()->CallObjectMethodA(_handle, method, (jvalue*) args);
445 handleJavaExceptions();
446 return Object(result, DeleteLocalInput);
447 }
448
449 template <> byte_t Object::get(field_t field) const
450 {
451 return env()->GetByteField(_handle, field);
452 }
453
454 template <> wchar_t Object::get(field_t field) const
455 {
456 return env()->GetCharField(_handle, field);
457 }
458
459 template <> short Object::get(field_t field) const
460 {
461 return env()->GetShortField(_handle, field);
462 }
463
464 template <> int Object::get(field_t field) const
465 {
466 return env()->GetIntField(_handle, field);
467 }
468
469 template <> long long Object::get(field_t field) const
470 {
471 return env()->GetLongField(_handle, field);
472 }
473
474 template <> float Object::get(field_t field) const
475 {
476 return env()->GetFloatField(_handle, field);
477 }
478
479 template <> double Object::get(field_t field) const
480 {
481 return env()->GetDoubleField(_handle, field);
482 }
483
484 template <> std::string Object::get(field_t field) const
485 {
486 return toString(env()->GetObjectField(_handle, field));
487 }
488
489 template <> std::wstring Object::get(field_t field) const
490 {
491 return toWString(env()->GetObjectField(_handle, field));
492 }
493
494 template <> Object Object::get(field_t field) const
495 {
496 return Object(env()->GetObjectField(_handle, field), DeleteLocalInput);
497 }
498
499 template <> void Object::set(field_t field, const byte_t& value)
500 {
501 env()->SetByteField(_handle, field, value);
502 }
503
504 template <> void Object::set(field_t field, const wchar_t& value)
505 {
506 env()->SetCharField(_handle, field, value);
507 }
508
509 template <> void Object::set(field_t field, const short& value)
510 {
511 env()->SetShortField(_handle, field, value);
512 }
513
514 template <> void Object::set(field_t field, const int& value)
515 {
516 env()->SetIntField(_handle, field, value);
517 }
518
519 template <> void Object::set(field_t field, const long long& value)
520 {
521 env()->SetLongField(_handle, field, value);
522 }
523
524 template <> void Object::set(field_t field, const float& value)
525 {
526 env()->SetFloatField(_handle, field, value);
527 }
528
529 template <> void Object::set(field_t field, const double& value)
530 {
531 env()->SetDoubleField(_handle, field, value);
532 }
533
534 template <> void Object::set(field_t field, const std::string& value)
535 {
536 JNIEnv* env = jni::env();
537
538 jobject handle = env->NewStringUTF(value.c_str());
539 env->SetObjectField(_handle, field, handle);
540 env->DeleteLocalRef(handle);
541 }
542
543 template <> void Object::set(field_t field, const std::wstring& value)
544 {
545 JNIEnv* env = jni::env();
546
547#ifdef _WIN32
548 jobject handle = env->NewString((const jchar*) value.c_str(), jsize(value.length()));
549#else
550 auto jstr = toJString(value.c_str(), value.length());
551 jobject handle = env->NewString(jstr.c_str(), jsize(jstr.length()));
552#endif
553 env->SetObjectField(_handle, field, handle);
554 env->DeleteLocalRef(handle);
555 }
556
557 template <> void Object::set(field_t field, const wchar_t* const& value)
558 {
559 JNIEnv* env = jni::env();
560#ifdef _WIN32
561 jobject handle = env->NewString((const jchar*) value, jsize(std::wcslen(value)));
562#else
563 auto jstr = toJString(value, std::wcslen(value));
564 jobject handle = env->NewString(jstr.c_str(), jsize(jstr.length()));
565#endif
566 env->SetObjectField(_handle, field, handle);
567 env->DeleteLocalRef(handle);
568 }
569
570 template <> void Object::set(field_t field, const char* const& value)
571 {
572 JNIEnv* env = jni::env();
573
574 jobject handle = env->NewStringUTF(value);
575 env->SetObjectField(_handle, field, handle);
576 env->DeleteLocalRef(handle);
577 }
578
579 template <> void Object::set(field_t field, const Object& value)
580 {
581 env()->SetObjectField(_handle, field, value.getHandle());
582 }
583
584 template <> void Object::set(field_t field, const Object* const& value)
585 {
586 env()->SetObjectField(_handle, field, value ? value->getHandle() : nullptr);
587 }
588
589 jclass Object::getClass() const
590 {
591 if (_class == nullptr)
592 {
593 JNIEnv* env = jni::env();
594
595 jclass classRef = env->GetObjectClass(_handle);
596 _class = jclass(env->NewGlobalRef(classRef));
597 env->DeleteLocalRef(classRef);
598 }
599
600 return _class;
601 }
602
603 method_t Object::getMethod(const char* name, const char* signature) const
604 {
605 return Class(getClass(), Temporary).getMethod(name, signature);
606 }
607
608 method_t Object::getMethod(const char* nameAndSignature) const
609 {
610 return Class(getClass(), Temporary).getMethod(nameAndSignature);
611 }
612
613 field_t Object::getField(const char* name, const char* signature) const
614 {
615 return Class(getClass(), Temporary).getField(name, signature);
616 }
617
618 jobject Object::makeLocalReference() const
619 {
620 if (isNull())
621 return nullptr;
622 return env()->NewLocalRef(_handle);
623 }
624
625 /*
626 Class Implementation
627 */
628
629 Class::Class(const char* name) : Object(findClass(name), DeleteLocalInput)
630 {
631 }
632
633 Class::Class(jclass ref, int scopeFlags) : Object(ref, scopeFlags)
634 {
635 }
636
637 Object Class::newInstance() const
638 {
639 method_t constructor = getMethod("<init>", "()V");
640 jobject obj = env()->NewObject(getHandle(), constructor);
641
642 handleJavaExceptions();
643
644 return Object(obj, Object::DeleteLocalInput);
645 }
646
647 field_t Class::getField(const char* name, const char* signature) const
648 {
649 jfieldID id = env()->GetFieldID(getHandle(), name, signature);
650
651 if (id == nullptr)
652 throw NameResolutionException(name);
653
654 return id;
655 }
656
657 field_t Class::getStaticField(const char* name, const char* signature) const
658 {
659 jfieldID id = env()->GetStaticFieldID(getHandle(), name, signature);
660
661 if (id == nullptr)
662 throw NameResolutionException(name);
663
664 return id;
665 }
666
667 method_t Class::getMethod(const char* name, const char* signature) const
668 {
669 jmethodID id = env()->GetMethodID(getHandle(), name, signature);
670
671 if (id == nullptr)
672 throw NameResolutionException(name);
673
674 return id;
675 }
676
677
678 method_t Class::getMethod(const char* nameAndSignature) const
679 {
680 jmethodID id = nullptr;
681 const char* sig = std::strchr(nameAndSignature, '(');
682
683 if (sig != nullptr)
684 return getMethod(std::string(nameAndSignature, sig - nameAndSignature).c_str(), sig);
685
686 if (id == nullptr)
687 throw NameResolutionException(nameAndSignature);
688
689 return id;
690 }
691
692 method_t Class::getStaticMethod(const char* name, const char* signature) const
693 {
694 jmethodID id = env()->GetStaticMethodID(getHandle(), name, signature);
695
696 if (id == nullptr)
697 throw NameResolutionException(name);
698
699 return id;
700 }
701
702 method_t Class::getStaticMethod(const char* nameAndSignature) const
703 {
704 jmethodID id = nullptr;
705 const char* sig = std::strchr(nameAndSignature, '(');
706
707 if (sig != nullptr)
708 return getStaticMethod(std::string(nameAndSignature, sig - nameAndSignature).c_str(), sig);
709
710 if (id == nullptr)
711 throw NameResolutionException(nameAndSignature);
712
713 return id;
714 }
715
716 Class Class::getParent() const
717 {
718 return Class(env()->GetSuperclass(getHandle()), DeleteLocalInput);
719 }
720
721 std::string Class::getName() const
722 {
723 return Object::call<std::string>("getName");
724 }
725
726 template <> bool Class::get(field_t field) const
727 {
728 return env()->GetStaticBooleanField(getHandle(), field) != 0;
729 }
730
731 template <> byte_t Class::get(field_t field) const
732 {
733 return env()->GetStaticByteField(getHandle(), field);
734 }
735
736 template <> wchar_t Class::get(field_t field) const
737 {
738 return env()->GetStaticCharField(getHandle(), field);
739 }
740
741 template <> short Class::get(field_t field) const
742 {
743 return env()->GetStaticShortField(getHandle(), field);
744 }
745
746 template <> int Class::get(field_t field) const
747 {
748 return env()->GetStaticIntField(getHandle(), field);
749 }
750
751 template <> long long Class::get(field_t field) const
752 {
753 return env()->GetStaticLongField(getHandle(), field);
754 }
755
756 template <> float Class::get(field_t field) const
757 {
758 return env()->GetStaticFloatField(getHandle(), field);
759 }
760
761 template <> double Class::get(field_t field) const
762 {
763 return env()->GetStaticDoubleField(getHandle(), field);
764 }
765
766 template <> std::string Class::get(field_t field) const
767 {
768 return toString(env()->GetStaticObjectField(getHandle(), field));
769 }
770
771 template <> std::wstring Class::get(field_t field) const
772 {
773 return toWString(env()->GetStaticObjectField(getHandle(), field));
774 }
775
776 template <> Object Class::get(field_t field) const
777 {
778 return Object(env()->GetStaticObjectField(getHandle(), field), DeleteLocalInput);
779 }
780
781 template <> void Class::set(field_t field, const bool& value)
782 {
783 env()->SetStaticBooleanField(getHandle(), field, value);
784 }
785
786 template <> void Class::set(field_t field, const byte_t& value)
787 {
788 env()->SetStaticByteField(getHandle(), field, value);
789 }
790
791 template <> void Class::set(field_t field, const wchar_t& value)
792 {
793 env()->SetStaticCharField(getHandle(), field, value);
794 }
795
796 template <> void Class::set(field_t field, const short& value)
797 {
798 env()->SetStaticShortField(getHandle(), field, value);
799 }
800
801 template <> void Class::set(field_t field, const int& value)
802 {
803 env()->SetStaticIntField(getHandle(), field, value);
804 }
805
806 template <> void Class::set(field_t field, const long long& value)
807 {
808 env()->SetStaticLongField(getHandle(), field, value);
809 }
810
811 template <> void Class::set(field_t field, const float& value)
812 {
813 env()->SetStaticFloatField(getHandle(), field, value);
814 }
815
816 template <> void Class::set(field_t field, const double& value)
817 {
818 env()->SetStaticDoubleField(getHandle(), field, value);
819 }
820
821 template <> void Class::set(field_t field, const Object& value)
822 {
823 env()->SetStaticObjectField(getHandle(), field, value.getHandle());
824 }
825
826 template <> void Class::set(field_t field, const Object* const& value)
827 {
828 env()->SetStaticObjectField(getHandle(), field, value ? value->getHandle() : nullptr);
829 }
830
831 template <> void Class::set(field_t field, const std::string& value)
832 {
833 JNIEnv* env = jni::env();
834
835 jobject handle = env->NewStringUTF(value.c_str());
836 env->SetStaticObjectField(getHandle(), field, handle);
837 env->DeleteLocalRef(handle);
838 }
839
840 template <> void Class::set(field_t field, const std::wstring& value)
841 {
842 JNIEnv* env = jni::env();
843
844#ifdef _WIN32
845 jobject handle = env->NewString((const jchar*) value.c_str(), jsize(value.length()));
846#else
847 auto jstr = toJString(value.c_str(), value.length());
848 jobject handle = env->NewString(jstr.c_str(), jsize(jstr.length()));
849#endif
850 env->SetStaticObjectField(getHandle(), field, handle);
851 env->DeleteLocalRef(handle);
852 }
853
854 template <> void Class::callStaticMethod(method_t method, internal::value_t* args) const
855 {
856 env()->CallStaticVoidMethodA(getHandle(), method, (jvalue*) args);
857 handleJavaExceptions();
858 }
859
860 template <> bool Class::callStaticMethod(method_t method, internal::value_t* args) const
861 {
862 auto result = env()->CallStaticBooleanMethodA(getHandle(), method, (jvalue*) args);
863 handleJavaExceptions();
864 return result != 0;
865 }
866
867 template <> byte_t Class::callStaticMethod(method_t method, internal::value_t* args) const
868 {
869 auto result = env()->CallStaticByteMethodA(getHandle(), method, (jvalue*) args);
870 handleJavaExceptions();
871 return result;
872 }
873
874 template <> wchar_t Class::callStaticMethod(method_t method, internal::value_t* args) const
875 {
876 auto result = env()->CallStaticCharMethodA(getHandle(), method, (jvalue*) args);
877 handleJavaExceptions();
878 return result;
879 }
880
881 template <> short Class::callStaticMethod(method_t method, internal::value_t* args) const
882 {
883 auto result = env()->CallStaticShortMethodA(getHandle(), method, (jvalue*) args);
884 handleJavaExceptions();
885 return result;
886 }
887
888 template <> int Class::callStaticMethod(method_t method, internal::value_t* args) const
889 {
890 auto result = env()->CallStaticIntMethodA(getHandle(), method, (jvalue*) args);
891 handleJavaExceptions();
892 return result;
893 }
894
895 template <> long long Class::callStaticMethod(method_t method, internal::value_t* args) const
896 {
897 auto result = env()->CallStaticLongMethodA(getHandle(), method, (jvalue*) args);
898 handleJavaExceptions();
899 return result;
900 }
901
902 template <> float Class::callStaticMethod(method_t method, internal::value_t* args) const
903 {
904 auto result = env()->CallStaticFloatMethodA(getHandle(), method, (jvalue*) args);
905 handleJavaExceptions();
906 return result;
907 }
908
909 template <> double Class::callStaticMethod(method_t method, internal::value_t* args) const
910 {
911 auto result = env()->CallStaticDoubleMethodA(getHandle(), method, (jvalue*) args);
912 handleJavaExceptions();
913 return result;
914 }
915
916 template <> std::string Class::callStaticMethod(method_t method, internal::value_t* args) const
917 {
918 auto result = env()->CallStaticObjectMethodA(getHandle(), method, (jvalue*) args);
919 handleJavaExceptions();
920 return toString(result);
921 }
922
923 template <> std::wstring Class::callStaticMethod(method_t method, internal::value_t* args) const
924 {
925 auto result = env()->CallStaticObjectMethodA(getHandle(), method, (jvalue*) args);
926 handleJavaExceptions();
927 return toWString(result);
928 }
929
930 template <> jni::Object Class::callStaticMethod(method_t method, internal::value_t* args) const
931 {
932 auto result = env()->CallStaticObjectMethodA(getHandle(), method, (jvalue*) args);
933 handleJavaExceptions();
934 return Object(result, DeleteLocalInput);
935 }
936
937 template <> void Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
938 {
939 env()->CallNonvirtualVoidMethodA(obj, getHandle(), method, (jvalue*) args);
940 handleJavaExceptions();
941 }
942
943 template <> bool Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
944 {
945 auto result = env()->CallNonvirtualBooleanMethodA(obj, getHandle(), method, (jvalue*) args);
946 handleJavaExceptions();
947 return result != 0;
948 }
949
950 template <> byte_t Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
951 {
952 auto result = env()->CallNonvirtualByteMethodA(obj, getHandle(), method, (jvalue*) args);
953 handleJavaExceptions();
954 return result;
955 }
956
957 template <> wchar_t Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
958 {
959 auto result = env()->CallNonvirtualCharMethodA(obj, getHandle(), method, (jvalue*) args);
960 handleJavaExceptions();
961 return result;
962 }
963
964 template <> short Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
965 {
966 auto result = env()->CallNonvirtualShortMethodA(obj, getHandle(), method, (jvalue*) args);
967 handleJavaExceptions();
968 return result;
969 }
970
971 template <> int Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
972 {
973 auto result = env()->CallNonvirtualIntMethodA(obj, getHandle(), method, (jvalue*) args);
974 handleJavaExceptions();
975 return result;
976 }
977
978 template <> long long Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
979 {
980 auto result = env()->CallNonvirtualLongMethodA(obj, getHandle(), method, (jvalue*) args);
981 handleJavaExceptions();
982 return result;
983 }
984
985 template <> float Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
986 {
987 auto result = env()->CallNonvirtualFloatMethodA(obj, getHandle(), method, (jvalue*) args);
988 handleJavaExceptions();
989 return result;
990 }
991
992 template <> double Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
993 {
994 auto result = env()->CallNonvirtualDoubleMethodA(obj, getHandle(), method, (jvalue*) args);
995 handleJavaExceptions();
996 return result;
997 }
998
999 template <> std::string Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
1000 {
1001 auto result = env()->CallNonvirtualObjectMethodA(obj, getHandle(), method, (jvalue*)args);
1002 handleJavaExceptions();
1003 return toString(result);
1004 }
1005
1006 template <> std::wstring Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
1007 {
1008 auto result = env()->CallNonvirtualObjectMethodA(obj, getHandle(), method, (jvalue*)args);
1009 handleJavaExceptions();
1010 return toWString(result);
1011 }
1012
1013 template <> Object Class::callExactMethod(jobject obj, method_t method, internal::value_t* args) const
1014 {
1015 auto result = env()->CallNonvirtualObjectMethodA(obj, getHandle(), method, (jvalue*)args);
1016 handleJavaExceptions();
1017 return Object(result, DeleteLocalInput);
1018 }
1019
1020 Object Class::newObject(method_t constructor, internal::value_t* args) const
1021 {
1022 jobject ref = env()->NewObjectA(getHandle(), constructor, (jvalue*)args);
1023 handleJavaExceptions();
1024 return Object(ref, DeleteLocalInput);
1025 }
1026
1027 /*
1028 Enum Implementation
1029 */
1030
1031 Enum::Enum(const char* name) : Class(name)
1032 {
1033 _name = "L";
1034 _name += name;
1035 _name += ";";
1036 }
1037
1038 Object Enum::get(const char* name) const
1039 {
1040 return Class::get<Object>(getStaticField(name, _name.c_str()));
1041 }
1042
1043 /*
1044 Array Implementation
1045 */
1046
1047 template <> Array<bool>::Array(long length) : Object(env()->NewBooleanArray(length)), _length(length)
1048 {
1049 }
1050
1051 template <> Array<byte_t>::Array(long length) : Object(env()->NewByteArray(length)), _length(length)
1052 {
1053 }
1054
1055 template <> Array<wchar_t>::Array(long length) : Object(env()->NewCharArray(length)), _length(length)
1056 {
1057 }
1058
1059 template <> Array<short>::Array(long length) : Object(env()->NewShortArray(length)), _length(length)
1060 {
1061 }
1062
1063 template <> Array<int>::Array(long length) : Object(env()->NewIntArray(length)), _length(length)
1064 {
1065 }
1066
1067 template <> Array<long long>::Array(long length) : Object(env()->NewLongArray(length)), _length(length)
1068 {
1069 }
1070
1071 template <> Array<float>::Array(long length) : Object(env()->NewFloatArray(length)), _length(length)
1072 {
1073 }
1074
1075 template <> Array<double>::Array(long length) : Object(env()->NewDoubleArray(length)), _length(length)
1076 {
1077 }
1078
1079 template <> Array<std::string>::Array(long length) : Object(env()->NewObjectArray(length, Class("java/lang/String").getHandle(), nullptr)), _length(length)
1080 {
1081 }
1082
1083 template <> Array<std::wstring>::Array(long length) : Object(env()->NewObjectArray(length, Class("java/lang/String").getHandle(), nullptr)), _length(length)
1084 {
1085 }
1086
1087 template <> Array<Object>::Array(long length) : Object(env()->NewObjectArray(length, Class("java/lang/Object").getHandle(), nullptr)), _length(length)
1088 {
1089 }
1090
1091 template <> Array<Object>::Array(long length, const Class& type) : Object(env()->NewObjectArray(length, type.getHandle(), nullptr)), _length(length)
1092 {
1093 }
1094
1095 template <> bool Array<bool>::getElement(long index) const
1096 {
1097 jboolean output;
1098 env()->GetBooleanArrayRegion(jbooleanArray(getHandle()), index, 1, &output);
1099 handleJavaExceptions();
1100 return output;
1101 }
1102
1103 template <> byte_t Array<byte_t>::getElement(long index) const
1104 {
1105 jbyte output;
1106 env()->GetByteArrayRegion(jbyteArray(getHandle()), index, 1, &output);
1107 handleJavaExceptions();
1108 return output;
1109 }
1110
1111 template <> wchar_t Array<wchar_t>::getElement(long index) const
1112 {
1113 jchar output;
1114 env()->GetCharArrayRegion(jcharArray(getHandle()), index, 1, &output);
1115 handleJavaExceptions();
1116 return output;
1117 }
1118
1119 template <> short Array<short>::getElement(long index) const
1120 {
1121 jshort output;
1122 env()->GetShortArrayRegion(jshortArray(getHandle()), index, 1, &output);
1123 handleJavaExceptions();
1124 return output;
1125 }
1126
1127 template <> int Array<int>::getElement(long index) const
1128 {
1129 jint output;
1130 env()->GetIntArrayRegion(jintArray(getHandle()), index, 1, &output);
1131 handleJavaExceptions();
1132 return output;
1133 }
1134
1135 template <> long long Array<long long>::getElement(long index) const
1136 {
1137 jlong output;
1138 env()->GetLongArrayRegion(jlongArray(getHandle()), index, 1, &output);
1139 handleJavaExceptions();
1140 return output;
1141 }
1142
1143 template <> float Array<float>::getElement(long index) const
1144 {
1145 jfloat output;
1146 env()->GetFloatArrayRegion(jfloatArray(getHandle()), index, 1, &output);
1147 handleJavaExceptions();
1148 return output;
1149 }
1150
1151 template <> double Array<double>::getElement(long index) const
1152 {
1153 jdouble output;
1154 env()->GetDoubleArrayRegion(jdoubleArray(getHandle()), index, 1, &output);
1155 handleJavaExceptions();
1156 return output;
1157 }
1158
1159 template <> std::string Array<std::string>::getElement(long index) const
1160 {
1161 jobject output = env()->GetObjectArrayElement(jobjectArray(getHandle()), index);
1162 handleJavaExceptions();
1163 return toString(output);
1164 }
1165
1166 template <> std::wstring Array<std::wstring>::getElement(long index) const
1167 {
1168 jobject output = env()->GetObjectArrayElement(jobjectArray(getHandle()), index);
1169 handleJavaExceptions();
1170 return toWString(output);
1171 }
1172
1173 template <> Object Array<Object>::getElement(long index) const
1174 {
1175 jobject output = env()->GetObjectArrayElement(jobjectArray(getHandle()), index);
1176 handleJavaExceptions();
1177 return Object(output, DeleteLocalInput);
1178 }
1179
1180 template <> void Array<bool>::setElement(long index, bool value)
1181 {
1182 jboolean jvalue = value;
1183 env()->SetBooleanArrayRegion(jbooleanArray(getHandle()), index, 1, &jvalue);
1184 handleJavaExceptions();
1185 }
1186
1187 template <> void Array<byte_t>::setElement(long index, byte_t value)
1188 {
1189 jbyte jvalue = value;
1190 env()->SetByteArrayRegion(jbyteArray(getHandle()), index, 1, &jvalue);
1191 handleJavaExceptions();
1192 }
1193
1194 template <> void Array<wchar_t>::setElement(long index, wchar_t value)
1195 {
1196 jchar jvalue = value;
1197 env()->SetCharArrayRegion(jcharArray(getHandle()), index, 1, &jvalue);
1198 handleJavaExceptions();
1199 }
1200
1201 template <> void Array<short>::setElement(long index, short value)
1202 {
1203 jshort jvalue = value;
1204 env()->SetShortArrayRegion(jshortArray(getHandle()), index, 1, &jvalue);
1205 handleJavaExceptions();
1206 }
1207
1208 template <> void Array<int>::setElement(long index, int value)
1209 {
1210 jint jvalue = value;
1211 env()->SetIntArrayRegion(jintArray(getHandle()), index, 1, &jvalue);
1212 handleJavaExceptions();
1213 }
1214
1215 template <> void Array<long long>::setElement(long index, long long value)
1216 {
1217 jlong jvalue = value;
1218 env()->SetLongArrayRegion(jlongArray(getHandle()), index, 1, &jvalue);
1219 handleJavaExceptions();
1220 }
1221
1222 template <> void Array<float>::setElement(long index, float value)
1223 {
1224 jfloat jvalue = value;
1225 env()->SetFloatArrayRegion(jfloatArray(getHandle()), index, 1, &jvalue);
1226 handleJavaExceptions();
1227 }
1228
1229 template <> void Array<double>::setElement(long index, double value)
1230 {
1231 jdouble jvalue = value;
1232 env()->SetDoubleArrayRegion(jdoubleArray(getHandle()), index, 1, &jvalue);
1233 handleJavaExceptions();
1234 }
1235
1236 template <> void Array<std::string>::setElement(long index, std::string value)
1237 {
1238 JNIEnv* env = jni::env();
1239
1240 jobject jvalue = env->NewStringUTF(value.c_str());;
1241 env->SetObjectArrayElement(jobjectArray(getHandle()), index, jvalue);
1242 env->DeleteLocalRef(jvalue);
1243 handleJavaExceptions();
1244 }
1245
1246 template <> void Array<std::wstring>::setElement(long index, std::wstring value)
1247 {
1248 JNIEnv* env = jni::env();
1249
1250#ifdef _WIN32
1251 jobject jvalue = env->NewString((const jchar*) value.c_str(), jsize(value.length()));
1252#else
1253 auto jstr = toJString(value.c_str(), value.length());
1254 jobject jvalue = env->NewString(jstr.c_str(), jsize(jstr.length()));
1255#endif
1256 env->SetObjectArrayElement(jobjectArray(getHandle()), index, jvalue);
1257 env->DeleteLocalRef(jvalue);
1258 handleJavaExceptions();
1259 }
1260
1261 template <> void Array<Object>::setElement(long index, Object value)
1262 {
1263 env()->SetObjectArrayElement(jobjectArray(getHandle()), index, value.getHandle());
1264 handleJavaExceptions();
1265 }
1266
1267 /*
1268 Vm Implementation
1269 */
1270
1271 typedef jint (JNICALL *CreateVm_t)(JavaVM**, void**, void*);
1272
1273
1274 static std::string detectJvmPath()
1275 {
1276 std::string result;
1277
1278#ifdef _WIN32
1279
1280 BYTE buffer[1024];
1281 DWORD size = sizeof(buffer);
1282 HKEY versionKey;
1283
1284 // Search via registry entries.
1285 if (::RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\JavaSoft\\Java Runtime Environment\\", &versionKey) == ERROR_SUCCESS)
1286 {
1287 if (::RegQueryValueEx(versionKey, "CurrentVersion", NULL, NULL, buffer, &size) == ERROR_SUCCESS)
1288 {
1289 HKEY libKey;
1290
1291 std::string keyName = std::string("Software\\JavaSoft\\Java Runtime Environment\\") + (const char*)buffer;
1292
1293 ::RegCloseKey(versionKey);
1294
1295 if (::RegOpenKeyA(HKEY_LOCAL_MACHINE, keyName.c_str(), &libKey) == ERROR_SUCCESS)
1296 {
1297 size = sizeof(buffer);
1298
1299 if (::RegQueryValueEx(libKey, "RuntimeLib", NULL, NULL, buffer, &size) == ERROR_SUCCESS)
1300 {
1301 result = (const char*)buffer;
1302 }
1303
1304 ::RegCloseKey(libKey);
1305 }
1306 }
1307 }
1308
1309 if (result.length() == 0)
1310 {
1311 // Could not locate via registry. Try the JAVA_HOME environment variable.
1312 if ((size = ::GetEnvironmentVariableA("JAVA_HOME", (LPSTR) buffer, sizeof(buffer))) != 0)
1313 {
1314 std::string javaHome((const char*) buffer, size);
1315
1316 // Different installers put in different relative locations.
1317 std::string options[] = {
1318 javaHome + "\\jre\\bin\\client\\jvm.dll",
1319 javaHome + "\\jre\\bin\\server\\jvm.dll",
1320 javaHome + "\\bin\\client\\jvm.dll",
1321 javaHome + "\\bin\\server\\jvm.dll"
1322 };
1323
1324 for (auto i : options)
1325 if (fileExists(i))
1326 return i;
1327 }
1328 }
1329
1330#else
1331
1332 const char* javaHome = getenv("JAVA_HOME");
1333 if (javaHome != nullptr) {
1334 #ifdef __APPLE__
1335 std::string libJvmPath = std::string(javaHome) + "/jre/lib/server/libjvm.dylib";
1336 #else
1337 std::string libJvmPath = std::string(javaHome) + "/jre/lib/amd64/server/libjvm.so";
1338 #endif
1339 result = libJvmPath;
1340 } else {
1341 // Best guess so far.
1342 result = "/usr/lib/jvm/default-java/jre/lib/amd64/server/libjvm.so";
1343 }
1344
1345#endif // _WIN32
1346
1347 return result;
1348 }
1349
1350 Vm::Vm(const char* path_)
1351 {
1352 bool expected = false;
1353
1354 std::string path = path_ ? path_ : detectJvmPath();
1355
1356 if (path.length() == 0)
1357 throw InitializationException("Could not locate Java Virtual Machine");
1358 if (!isVm.compare_exchange_strong(expected, true))
1359 throw InitializationException("Java Virtual Machine already initialized");
1360
1361 if (javaVm == nullptr)
1362 {
1363 JNIEnv* env;
1364 JavaVMInitArgs args = {};
1365 args.version = JNI_VERSION_1_2;
1366
1367#ifdef _WIN32
1368
1369 HMODULE lib = ::LoadLibraryA(path.c_str());
1370
1371 if (lib == NULL)
1372 {
1373 isVm.store(false);
1374 throw InitializationException("Could not load JVM library");
1375 }
1376
1377 CreateVm_t JNI_CreateJavaVM = (CreateVm_t) ::GetProcAddress(lib, "JNI_CreateJavaVM");
1378
1379 /**
1380 Is your debugger catching an error here? This is normal. Just continue. The JVM
1381 intentionally does this to test how the OS handles memory-reference exceptions.
1382 */
1383 if (JNI_CreateJavaVM == NULL || JNI_CreateJavaVM(&javaVm, (void**) &env, &args) != 0)
1384 {
1385 isVm.store(false);
1386 ::FreeLibrary(lib);
1387 throw InitializationException("Java Virtual Machine failed during creation");
1388 }
1389
1390#else
1391
1392 void* lib = ::dlopen(path.c_str(), RTLD_NOW | RTLD_GLOBAL);
1393
1394 if (lib == NULL)
1395 {
1396 isVm.store(false);
1397 throw InitializationException("Could not load JVM library");
1398 }
1399
1400 CreateVm_t JNI_CreateJavaVM = (CreateVm_t) ::dlsym(lib, "JNI_CreateJavaVM");
1401
1402 if (JNI_CreateJavaVM == NULL || JNI_CreateJavaVM(&javaVm, (void**) &env, &args) != 0)
1403 {
1404 isVm.store(false);
1405 ::dlclose(lib);
1406 throw InitializationException("Java Virtual Machine failed during creation");
1407 }
1408
1409#endif // _WIN32
1410 }
1411 }
1412
1413 Vm::~Vm()
1414 {
1415 /*
1416 Note that you can't ever *really* unload the JavaVM. If you call
1417 DestroyJavaVM(), you can't then call JNI_CreateJavaVM() again.
1418 So, instead we just flag it as "gone".
1419 */
1420 isVm.store(false);
1421 }
1422
1423 // Forward Declarations
1424 JNIEnv* env();
1425
1426#ifndef _WIN32
1427 extern std::basic_string<jchar> toJString(const wchar_t* str, size_t length);
1428#endif
1429
1430 namespace internal
1431 {
1432 // Base Type Conversions
1433 void valueArg(value_t* v, bool a) { ((jvalue*) v)->z = jboolean(a); }
1434 void valueArg(value_t* v, byte_t a) { ((jvalue*) v)->b = a; }
1435 void valueArg(value_t* v, wchar_t a) { ((jvalue*) v)->c = jchar(a); } // Note: Possible truncation.
1436 void valueArg(value_t* v, short a) { ((jvalue*) v)->s = a; }
1437 void valueArg(value_t* v, int a) { ((jvalue*) v)->i = a; }
1438 void valueArg(value_t* v, long long a) { ((jvalue*) v)->j = a; }
1439 void valueArg(value_t* v, float a) { ((jvalue*) v)->f = a; }
1440 void valueArg(value_t* v, double a) { ((jvalue*) v)->d = a; }
1441 void valueArg(value_t* v, jobject a) { ((jvalue*) v)->l = a; }
1442 void valueArg(value_t* v, const Object& a) { ((jvalue*) v)->l = a.getHandle(); }
1443 void valueArg(value_t* v, const Object* const& a) { ((jvalue*) v)->l = a ? a->getHandle() : nullptr; }
1444
1445 /*
1446 Object Implementations
1447 */
1448
1449 std::string valueSig(const Object* obj)
1450 {
1451 if (obj == nullptr || obj->isNull())
1452 return "Ljava/lang/Object;"; // One can always hope...
1453
1454 std::string name = Class(obj->getClass(), Object::Temporary).getName();
1455
1456 // Change from "java.lang.Object" format to "java/lang/Object";
1457 for (size_t i = 0; i < name.length(); ++i)
1458 if (name[i] == '.')
1459 name[i] = '/';
1460
1461 return "L" + name + ";";
1462 }
1463
1464 /*
1465 String Implementations
1466 */
1467
1468 void valueArg(value_t* v, const std::string& a)
1469 {
1470 ((jvalue*) v)->l = env()->NewStringUTF(a.c_str());
1471 }
1472
1473 template <> void cleanupArg<std::string>(value_t* v)
1474 {
1475 env()->DeleteLocalRef(((jvalue*) v)->l);
1476 }
1477
1478 void valueArg(value_t* v, const char* a)
1479 {
1480 ((jvalue*) v)->l = env()->NewStringUTF(a);
1481 }
1482
1483
1484 template <> void cleanupArg<const char*>(value_t* v)
1485 {
1486 env()->DeleteLocalRef(((jvalue*) v)->l);
1487 }
1488#ifdef _WIN32
1489
1490 void valueArg(value_t* v, const std::wstring& a)
1491 {
1492 ((jvalue*) v)->l = env()->NewString((const jchar*) a.c_str(), jsize(a.length()));
1493 }
1494
1495 void valueArg(value_t* v, const wchar_t* a)
1496 {
1497 ((jvalue*) v)->l = env()->NewString((const jchar*) a, jsize(std::wcslen(a)));
1498 }
1499#else
1500
1501 void valueArg(value_t* v, const std::wstring& a)
1502 {
1503 auto jstr = toJString(a.c_str(), a.length());
1504 ((jvalue*) v)->l = env()->NewString(jstr.c_str(), jsize(jstr.length()));
1505 }
1506
1507 void valueArg(value_t* v, const wchar_t* a)
1508 {
1509 auto jstr = toJString(a, std::wcslen(a));
1510 ((jvalue*) v)->l = env()->NewString(jstr.c_str(), jsize(jstr.length()));
1511 }
1512
1513#endif
1514
1515 template <> void cleanupArg<const std::wstring*>(value_t* v)
1516 {
1517 env()->DeleteLocalRef(((jvalue*) v)->l);
1518 }
1519
1520 template <> void cleanupArg<const wchar_t*>(value_t* v)
1521 {
1522 env()->DeleteLocalRef(((jvalue*) v)->l);
1523 }
1524
1525 long getArrayLength(jarray array)
1526 {
1527 return env()->GetArrayLength(array);
1528 }
1529 }
1530}
1531