Skip to content

Commit e6c4158

Browse files
authored
Upstreaming3 (#36)
* Fix "empty array" warning * Improve JVM finding on Linux * Add flags to test build * Fix an issue if a client manually detaches our thread. * Add test for empty array bug. * Move away from explicit instantiation of function templates. Makes it too hard to track down errors: they showed up as linker errors, instead of compiler errors. * Fix null pointer exception if client manually attached the thread. Upstreamed from https://gitlab.freedesktop.org/monado/monado/-/merge_requests/1032 * CMake build system and extra tests
1 parent 60a3c86 commit e6c4158

File tree

8 files changed

+293
-32
lines changed

8 files changed

+293
-32
lines changed

CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright 2021, Collabora, Ltd.
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
cmake_minimum_required(VERSION 3.10.2)
6+
project(jnipp)
7+
8+
find_package(JNI REQUIRED)
9+
include(CTest)
10+
11+
add_library(jnipp jnipp.cpp)
12+
target_include_directories(
13+
jnipp
14+
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
15+
PRIVATE ${JNI_INCLUDE_DIRS})
16+
target_link_libraries(jnipp PUBLIC ${CMAKE_DL_LIBS})
17+
18+
add_subdirectory(tests)

jnipp.cpp

Lines changed: 116 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#else
77
// UNIX Dependencies
88
# include <dlfcn.h>
9+
# include <unistd.h>
10+
# include <tuple>
911
#endif
1012

1113
// External Dependencies
@@ -24,6 +26,14 @@ namespace jni
2426
static std::atomic_bool isVm(false);
2527
static JavaVM* javaVm = nullptr;
2628

29+
static bool getEnv(JavaVM *vm, JNIEnv **env) {
30+
return vm->GetEnv((void **)env, JNI_VERSION_1_2) == JNI_OK;
31+
}
32+
33+
static bool isAttached(JavaVM *vm) {
34+
JNIEnv *env = nullptr;
35+
return getEnv(vm, &env);
36+
}
2737
/**
2838
Maintains the lifecycle of a JNIEnv.
2939
*/
@@ -57,7 +67,7 @@ namespace jni
5767
if (vm == nullptr)
5868
throw InitializationException("JNI not initialized");
5969

60-
if (vm->GetEnv((void**)&_env, JNI_VERSION_1_2) != JNI_OK)
70+
if (!getEnv(vm, &_env))
6171
{
6272
#ifdef __ANDROID__
6373
if (vm->AttachCurrentThread(&_env, nullptr) != 0)
@@ -150,8 +160,17 @@ namespace jni
150160
{
151161
static thread_local ScopedEnv env;
152162

163+
if (env.get() != nullptr && !isAttached(javaVm))
164+
{
165+
// we got detached, so clear it.
166+
// will be re-populated from static javaVm below.
167+
env = ScopedEnv{};
168+
}
169+
153170
if (env.get() == nullptr)
171+
{
154172
env.init(javaVm);
173+
}
155174

156175
return env.get();
157176
}
@@ -353,20 +372,20 @@ namespace jni
353372
return _handle == nullptr || env()->IsSameObject(_handle, nullptr);
354373
}
355374

356-
template <> void Object::callMethod(method_t method, internal::value_t* args) const
375+
void Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<void> const&) const
357376
{
358377
env()->CallVoidMethodA(_handle, method, (jvalue*) args);
359378
handleJavaExceptions();
360379
}
361380

362-
template <> bool Object::callMethod(method_t method, internal::value_t* args) const
381+
bool Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<bool> const&) const
363382
{
364383
auto result = env()->CallBooleanMethodA(_handle, method, (jvalue*) args);
365384
handleJavaExceptions();
366385
return result != 0;
367386
}
368387

369-
template <> bool Object::get(field_t field) const
388+
bool Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<bool> const&) const
370389
{
371390
return env()->GetBooleanField(_handle, field) != 0;
372391
}
@@ -376,122 +395,122 @@ namespace jni
376395
env()->SetBooleanField(_handle, field, value);
377396
}
378397

379-
template <> byte_t Object::callMethod(method_t method, internal::value_t* args) const
398+
byte_t Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<byte_t> const&) const
380399
{
381400
auto result = env()->CallByteMethodA(_handle, method, (jvalue*) args);
382401
handleJavaExceptions();
383402
return result;
384403
}
385404

386-
template <> wchar_t Object::callMethod(method_t method, internal::value_t* args) const
405+
wchar_t Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<wchar_t> const&) const
387406
{
388407
auto result = env()->CallCharMethodA(_handle, method, (jvalue*) args);
389408
handleJavaExceptions();
390409
return result;
391410
}
392411

393-
template <> short Object::callMethod(method_t method, internal::value_t* args) const
412+
short Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<short> const&) const
394413
{
395414
auto result = env()->CallShortMethodA(_handle, method, (jvalue*) args);
396415
handleJavaExceptions();
397416
return result;
398417
}
399418

400-
template <> int Object::callMethod(method_t method, internal::value_t* args) const
419+
int Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<int> const&) const
401420
{
402421
auto result = env()->CallIntMethodA(_handle, method, (jvalue*) args);
403422
handleJavaExceptions();
404423
return result;
405424
}
406425

407-
template <> long long Object::callMethod(method_t method, internal::value_t* args) const
426+
long long Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<long long> const&) const
408427
{
409428
auto result = env()->CallLongMethodA(_handle, method, (jvalue*) args);
410429
handleJavaExceptions();
411430
return result;
412431
}
413432

414-
template <> float Object::callMethod(method_t method, internal::value_t* args) const
433+
float Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<float> const&) const
415434
{
416435
auto result = env()->CallFloatMethodA(_handle, method, (jvalue*) args);
417436
handleJavaExceptions();
418437
return result;
419438
}
420439

421-
template <> double Object::callMethod(method_t method, internal::value_t* args) const
440+
double Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<double> const&) const
422441
{
423442
auto result = env()->CallDoubleMethodA(_handle, method, (jvalue*) args);
424443
handleJavaExceptions();
425444
return result;
426445
}
427446

428-
template <> std::string Object::callMethod(method_t method, internal::value_t* args) const
447+
std::string Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<std::string> const&) const
429448
{
430449
auto result = env()->CallObjectMethodA(_handle, method, (jvalue*) args);
431450
handleJavaExceptions();
432451
return toString(result);
433452
}
434453

435-
template <> std::wstring Object::callMethod(method_t method, internal::value_t* args) const
454+
std::wstring Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<std::wstring> const&) const
436455
{
437456
auto result = env()->CallObjectMethodA(_handle, method, (jvalue*) args);
438457
handleJavaExceptions();
439458
return toWString(result);
440459
}
441460

442-
template <> jni::Object Object::callMethod(method_t method, internal::value_t* args) const
461+
jni::Object Object::callMethod(method_t method, internal::value_t* args, internal::ReturnTypeWrapper<jni::Object> const&) const
443462
{
444463
auto result = env()->CallObjectMethodA(_handle, method, (jvalue*) args);
445464
handleJavaExceptions();
446465
return Object(result, DeleteLocalInput);
447466
}
448467

449-
template <> byte_t Object::get(field_t field) const
468+
byte_t Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<byte_t> const&) const
450469
{
451470
return env()->GetByteField(_handle, field);
452471
}
453472

454-
template <> wchar_t Object::get(field_t field) const
473+
wchar_t Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<wchar_t> const&) const
455474
{
456475
return env()->GetCharField(_handle, field);
457476
}
458477

459-
template <> short Object::get(field_t field) const
478+
short Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<short> const&) const
460479
{
461480
return env()->GetShortField(_handle, field);
462481
}
463482

464-
template <> int Object::get(field_t field) const
483+
int Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<int> const&) const
465484
{
466485
return env()->GetIntField(_handle, field);
467486
}
468487

469-
template <> long long Object::get(field_t field) const
488+
long long Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<long long> const&) const
470489
{
471490
return env()->GetLongField(_handle, field);
472491
}
473492

474-
template <> float Object::get(field_t field) const
493+
float Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<float> const&) const
475494
{
476495
return env()->GetFloatField(_handle, field);
477496
}
478497

479-
template <> double Object::get(field_t field) const
498+
double Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<double> const&) const
480499
{
481500
return env()->GetDoubleField(_handle, field);
482501
}
483502

484-
template <> std::string Object::get(field_t field) const
503+
std::string Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<std::string> const&) const
485504
{
486505
return toString(env()->GetObjectField(_handle, field));
487506
}
488507

489-
template <> std::wstring Object::get(field_t field) const
508+
std::wstring Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<std::wstring> const&) const
490509
{
491510
return toWString(env()->GetObjectField(_handle, field));
492511
}
493512

494-
template <> Object Object::get(field_t field) const
513+
Object Object::getFieldValue(field_t field, internal::ReturnTypeWrapper<Object> const&) const
495514
{
496515
return Object(env()->GetObjectField(_handle, field), DeleteLocalInput);
497516
}
@@ -1270,7 +1289,57 @@ namespace jni
12701289

12711290
typedef jint (JNICALL *CreateVm_t)(JavaVM**, void**, void*);
12721291

1292+
#ifndef _WIN32
1293+
static bool fileExists(const std::string& filePath)
1294+
{
1295+
return access(filePath.c_str(), F_OK) != -1;
1296+
}
12731297

1298+
template <size_t N>
1299+
static ssize_t readlink_safe(const char *pathname, char (&output)[N]) {
1300+
auto len = readlink(pathname, output, N - 1);
1301+
if (len > 0) {
1302+
output[len] = '\0';
1303+
}
1304+
return len;
1305+
}
1306+
1307+
static std::pair<ssize_t, std::string>
1308+
readlink_as_string(const char *pathname) {
1309+
char buf[2048] = {};
1310+
auto len = readlink_safe(pathname, buf);
1311+
if (len <= 0) {
1312+
return {len, {}};
1313+
}
1314+
return {len, std::string{buf, static_cast<size_t>(len)}};
1315+
}
1316+
static std::string readlink_deep(const char *pathname) {
1317+
std::string prev{pathname};
1318+
ssize_t len = 0;
1319+
std::string next;
1320+
while (true) {
1321+
std::tie(len, next) = readlink_as_string(prev.c_str());
1322+
if (!next.empty()) {
1323+
prev = next;
1324+
} else {
1325+
return prev;
1326+
}
1327+
}
1328+
}
1329+
1330+
static std::string drop_path_components(const std::string & path, size_t num_components) {
1331+
size_t pos = std::string::npos;
1332+
size_t slash_pos = std::string::npos;
1333+
for (size_t i = 0; i < num_components; ++i) {
1334+
slash_pos = path.find_last_of('/', pos);
1335+
if (slash_pos == std::string::npos || slash_pos == 0) {
1336+
return {};
1337+
}
1338+
pos = slash_pos - 1;
1339+
}
1340+
return std::string{path.c_str(), slash_pos};
1341+
}
1342+
#endif
12741343
static std::string detectJvmPath()
12751344
{
12761345
std::string result;
@@ -1321,7 +1390,7 @@ namespace jni
13211390
javaHome + "\\bin\\server\\jvm.dll"
13221391
};
13231392

1324-
for (auto i : options)
1393+
for (auto const& i : options)
13251394
if (fileExists(i))
13261395
return i;
13271396
}
@@ -1338,6 +1407,28 @@ namespace jni
13381407
#endif
13391408
result = libJvmPath;
13401409
} else {
1410+
std::string path = readlink_deep("/usr/bin/java");
1411+
if (!path.empty()) {
1412+
// drop bin and java
1413+
auto javaHome = drop_path_components(path, 2);
1414+
if (!javaHome.empty()) {
1415+
std::string options[] = {
1416+
javaHome + "/jre/lib/amd64/server/libjvm.so",
1417+
javaHome + "/jre/lib/amd64/client/libjvm.so",
1418+
javaHome + "/jre/lib/server/libjvm.so",
1419+
javaHome + "/jre/lib/client/libjvm.so",
1420+
javaHome + "/lib/server/libjvm.so",
1421+
javaHome + "/lib/client/libjvm.so",
1422+
};
1423+
1424+
for (auto const &i : options) {
1425+
fprintf(stderr, "trying %s\n", i.c_str());
1426+
if (fileExists(i)) {
1427+
return i;
1428+
}
1429+
}
1430+
}
1431+
}
13411432
// Best guess so far.
13421433
result = "/usr/lib/jvm/default-java/jre/lib/amd64/server/libjvm.so";
13431434
}

0 commit comments

Comments
 (0)