Cleanup JNI code to always correctly free memory when loading fails and also correctly respect out of memory in all cases (#9596)

Motivation:

At the moment we not consistently (and also not correctly) free allocated native memory in all cases during loading the JNI library. This can lead to native memory leaks in the unlikely case of failure while trying to load the library.

Beside this we also not always correctly handle the case when a new java object can not be created in native code because of out of memory.

Modification:

- Copy some macros from netty-tcnative to be able to handle errors in a more easy fashion
- Correctly account for New* functions to return NULL
- Share code

Result:

More robust and clean JNI code
This commit is contained in:
Norman Maurer 2019-09-24 07:18:35 +02:00 committed by GitHub
parent eb3c4bd926
commit 5e69a13c21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 423 additions and 519 deletions

View File

@ -711,113 +711,83 @@ static jint dynamicMethodsTableSize() {
} }
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize()); char* dynamicTypeName = NULL;
size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize();
JNINativeMethod* dynamicMethods = malloc(size);
if (dynamicMethods == NULL) {
return NULL;
}
memset(dynamicMethods, 0, size);
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table)); memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size]; JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials;"); NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials;", dynamicTypeName, error);
NETTY_PREPEND("(I)L", dynamicTypeName, dynamicMethod->signature, error);
dynamicMethod->name = "getPeerCredentials"; dynamicMethod->name = "getPeerCredentials";
dynamicMethod->signature = netty_unix_util_prepend("(I)L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_getPeerCredentials; dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_getPeerCredentials;
free(dynamicTypeName); netty_unix_util_free_dynamic_name(&dynamicTypeName);
++dynamicMethod; ++dynamicMethod;
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J"); NETTY_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J", dynamicTypeName, error);
NETTY_PREPEND("(IL", dynamicTypeName, dynamicMethod->signature, error);
dynamicMethod->name = "sendFile"; dynamicMethod->name = "sendFile";
dynamicMethod->signature = netty_unix_util_prepend("(IL", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_sendFile; dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_sendFile;
free(dynamicTypeName); netty_unix_util_free_dynamic_name(&dynamicTypeName);
return dynamicMethods; return dynamicMethods;
error:
free(dynamicTypeName);
netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
return NULL;
} }
static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) {
jint fullMethodTableSize = dynamicMethodsTableSize();
jint i = fixed_method_table_size;
for (; i < fullMethodTableSize; ++i) {
free(dynamicMethods[i].signature);
}
free(dynamicMethods);
}
// JNI Method Registration Table End // JNI Method Registration Table End
jint netty_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { jint netty_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
int ret = JNI_ERR;
char* nettyClassName = NULL;
jclass fileRegionCls = NULL;
jclass fileChannelCls = NULL;
jclass fileDescriptorCls = NULL;
// Register the methods which are not referenced by static member variables
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
if (dynamicMethods == NULL) {
goto done;
}
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/epoll/LinuxSocket", "io/netty/channel/epoll/LinuxSocket",
dynamicMethods, dynamicMethods,
dynamicMethodsTableSize()) != 0) { dynamicMethodsTableSize()) != 0) {
freeDynamicMethodsTable(dynamicMethods); goto done;
return JNI_ERR;
} }
freeDynamicMethodsTable(dynamicMethods);
dynamicMethods = NULL;
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials"); NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials", nettyClassName, done);
jclass localPeerCredsClass = (*env)->FindClass(env, nettyClassName); NETTY_LOAD_CLASS(env, peerCredentialsClass, nettyClassName, done);
netty_unix_util_free_dynamic_name(&nettyClassName);
NETTY_GET_METHOD(env, peerCredentialsClass, peerCredentialsMethodId, "<init>", "(II[I)V", done);
NETTY_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion", nettyClassName, done);
NETTY_FIND_CLASS(env, fileRegionCls, nettyClassName, done);
netty_unix_util_free_dynamic_name(&nettyClassName);
NETTY_GET_FIELD(env, fileRegionCls, fileChannelFieldId, "file", "Ljava/nio/channels/FileChannel;", done);
NETTY_GET_FIELD(env, fileRegionCls, transferredFieldId, "transferred", "J", done);
NETTY_FIND_CLASS(env, fileChannelCls, "sun/nio/ch/FileChannelImpl", done);
NETTY_GET_FIELD(env, fileChannelCls, fileDescriptorFieldId, "fd", "Ljava/io/FileDescriptor;", done);
NETTY_FIND_CLASS(env, fileDescriptorCls, "java/io/FileDescriptor", done);
NETTY_GET_FIELD(env, fileDescriptorCls, fdFieldId, "fd", "I", done);
ret = NETTY_JNI_VERSION;
done:
netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
free(nettyClassName); free(nettyClassName);
nettyClassName = NULL;
if (localPeerCredsClass == NULL) {
// pending exception...
return JNI_ERR;
}
peerCredentialsClass = (jclass) (*env)->NewGlobalRef(env, localPeerCredsClass);
if (peerCredentialsClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
peerCredentialsMethodId = (*env)->GetMethodID(env, peerCredentialsClass, "<init>", "(II[I)V");
if (peerCredentialsMethodId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: PeerCredentials.<init>(int, int, int[])");
return JNI_ERR;
}
nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion"); return ret;
jclass fileRegionCls = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (fileRegionCls == NULL) {
return JNI_ERR;
}
fileChannelFieldId = (*env)->GetFieldID(env, fileRegionCls, "file", "Ljava/nio/channels/FileChannel;");
if (fileChannelFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.file");
return JNI_ERR;
}
transferredFieldId = (*env)->GetFieldID(env, fileRegionCls, "transferred", "J");
if (transferredFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.transferred");
return JNI_ERR;
}
jclass fileChannelCls = (*env)->FindClass(env, "sun/nio/ch/FileChannelImpl");
if (fileChannelCls == NULL) {
// pending exception...
return JNI_ERR;
}
fileDescriptorFieldId = (*env)->GetFieldID(env, fileChannelCls, "fd", "Ljava/io/FileDescriptor;");
if (fileDescriptorFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileChannelImpl.fd");
return JNI_ERR;
}
jclass fileDescriptorCls = (*env)->FindClass(env, "java/io/FileDescriptor");
if (fileDescriptorCls == NULL) {
// pending exception...
return JNI_ERR;
}
fdFieldId = (*env)->GetFieldID(env, fileDescriptorCls, "fd", "I");
if (fdFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileDescriptor.fd");
return JNI_ERR;
}
return NETTY_JNI_VERSION;
} }
void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env) { void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env) {
if (peerCredentialsClass != NULL) { NETTY_UNLOAD_CLASS(env, peerCredentialsClass);
(*env)->DeleteGlobalRef(env, peerCredentialsClass);
peerCredentialsClass = NULL;
}
} }

View File

@ -516,42 +516,48 @@ static jint dynamicMethodsTableSize() {
} }
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize()); char* dynamicTypeName = NULL;
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table)); size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize();
JNINativeMethod* dynamicMethods = malloc(size);
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I"); if (dynamicMethods == NULL) {
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size]; return NULL;
dynamicMethod->name = "sendmmsg0";
dynamicMethod->signature = netty_unix_util_prepend("(IZ[L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_epoll_native_sendmmsg0;
free(dynamicTypeName);
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I");
dynamicMethod = &dynamicMethods[fixed_method_table_size + 1];
dynamicMethod->name = "recvmmsg0";
dynamicMethod->signature = netty_unix_util_prepend("(IZ[L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_epoll_native_recvmmsg0;
free(dynamicTypeName);
return dynamicMethods;
}
static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) {
jint fullMethodTableSize = dynamicMethodsTableSize();
jint i = fixed_method_table_size;
for (; i < fullMethodTableSize; ++i) {
free(dynamicMethods[i].signature);
} }
free(dynamicMethods); memset(dynamicMethods, 0, size);
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
NETTY_PREPEND(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I", dynamicTypeName, error);
NETTY_PREPEND("(IZ[L", dynamicTypeName, dynamicMethod->signature, error);
dynamicMethod->name = "sendmmsg0";
dynamicMethod->fnPtr = (void *) netty_epoll_native_sendmmsg0;
netty_unix_util_free_dynamic_name(&dynamicTypeName);
++dynamicMethod;
NETTY_PREPEND(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I", dynamicTypeName, error);
NETTY_PREPEND("(IZ[L", dynamicTypeName, dynamicMethod->signature, error);
dynamicMethod->name = "recvmmsg0";
dynamicMethod->fnPtr = (void *) netty_epoll_native_recvmmsg0;
netty_unix_util_free_dynamic_name(&dynamicTypeName);
return dynamicMethods;
error:
free(dynamicTypeName);
netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
return NULL;
} }
// JNI Method Registration Table End // JNI Method Registration Table End
static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
int ret = JNI_ERR;
int limitsOnLoadCalled = 0; int limitsOnLoadCalled = 0;
int errorsOnLoadCalled = 0; int errorsOnLoadCalled = 0;
int filedescriptorOnLoadCalled = 0; int filedescriptorOnLoadCalled = 0;
int socketOnLoadCalled = 0; int socketOnLoadCalled = 0;
int bufferOnLoadCalled = 0; int bufferOnLoadCalled = 0;
int linuxsocketOnLoadCalled = 0; int linuxsocketOnLoadCalled = 0;
char* nettyClassName = NULL;
jclass nativeDatagramPacketCls = NULL;
// We must register the statically referenced methods first! // We must register the statically referenced methods first!
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
@ -559,122 +565,97 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix
"io/netty/channel/epoll/NativeStaticallyReferencedJniMethods", "io/netty/channel/epoll/NativeStaticallyReferencedJniMethods",
statically_referenced_fixed_method_table, statically_referenced_fixed_method_table,
statically_referenced_fixed_method_table_size) != 0) { statically_referenced_fixed_method_table_size) != 0) {
goto error; goto done;
} }
// Register the methods which are not referenced by static member variables // Register the methods which are not referenced by static member variables
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
if (dynamicMethods == NULL) {
goto done;
}
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/epoll/Native", "io/netty/channel/epoll/Native",
dynamicMethods, dynamicMethods,
dynamicMethodsTableSize()) != 0) { dynamicMethodsTableSize()) != 0) {
freeDynamicMethodsTable(dynamicMethods); goto done;
goto error;
} }
freeDynamicMethodsTable(dynamicMethods);
dynamicMethods = NULL;
// Load all c modules that we depend upon // Load all c modules that we depend upon
if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error; goto done;
} }
limitsOnLoadCalled = 1; limitsOnLoadCalled = 1;
if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error; goto done;
} }
errorsOnLoadCalled = 1; errorsOnLoadCalled = 1;
if (netty_unix_filedescriptor_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { if (netty_unix_filedescriptor_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error; goto done;
} }
filedescriptorOnLoadCalled = 1; filedescriptorOnLoadCalled = 1;
if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error; goto done;
} }
socketOnLoadCalled = 1; socketOnLoadCalled = 1;
if (netty_unix_buffer_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { if (netty_unix_buffer_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error; goto done;
} }
bufferOnLoadCalled = 1; bufferOnLoadCalled = 1;
if (netty_epoll_linuxsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { if (netty_epoll_linuxsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error; goto done;
} }
linuxsocketOnLoadCalled = 1; linuxsocketOnLoadCalled = 1;
// Initialize this module // Initialize this module
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket"); NETTY_PREPEND(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket", nettyClassName, done);
jclass nativeDatagramPacketCls = (*env)->FindClass(env, nettyClassName); NETTY_FIND_CLASS(env, nativeDatagramPacketCls, nettyClassName, done);
netty_unix_util_free_dynamic_name(&nettyClassName);
NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetAddrFieldId, "addr", "[B", done);
NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetAddrLenFieldId, "addrLen", "I", done);
NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetScopeIdFieldId, "scopeId", "I", done);
NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetPortFieldId, "port", "I", done);
NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetMemoryAddressFieldId, "memoryAddress", "J", done);
NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetCountFieldId, "count", "I", done);
ret = NETTY_JNI_VERSION;
done:
netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
free(nettyClassName); free(nettyClassName);
nettyClassName = NULL;
if (nativeDatagramPacketCls == NULL) {
// pending exception...
goto error;
}
packetAddrFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "addr", "[B"); if (ret == JNI_ERR) {
if (packetAddrFieldId == NULL) { if (limitsOnLoadCalled == 1) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.addr"); netty_unix_limits_JNI_OnUnLoad(env);
goto error; }
if (errorsOnLoadCalled == 1) {
netty_unix_errors_JNI_OnUnLoad(env);
}
if (filedescriptorOnLoadCalled == 1) {
netty_unix_filedescriptor_JNI_OnUnLoad(env);
}
if (socketOnLoadCalled == 1) {
netty_unix_socket_JNI_OnUnLoad(env);
}
if (bufferOnLoadCalled == 1) {
netty_unix_buffer_JNI_OnUnLoad(env);
}
if (linuxsocketOnLoadCalled == 1) {
netty_epoll_linuxsocket_JNI_OnUnLoad(env);
}
packetAddrFieldId = NULL;
packetAddrLenFieldId = NULL;
packetScopeIdFieldId = NULL;
packetPortFieldId = NULL;
packetMemoryAddressFieldId = NULL;
packetCountFieldId = NULL;
} }
packetAddrLenFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "addrLen", "I"); return ret;
if (packetAddrLenFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.addrLen");
goto error;
}
packetScopeIdFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "scopeId", "I");
if (packetScopeIdFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.scopeId");
goto error;
}
packetPortFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "port", "I");
if (packetPortFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.port");
goto error;
}
packetMemoryAddressFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "memoryAddress", "J");
if (packetMemoryAddressFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.memoryAddress");
goto error;
}
packetCountFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "count", "I");
if (packetCountFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.count");
goto error;
}
return NETTY_JNI_VERSION;
error:
if (limitsOnLoadCalled == 1) {
netty_unix_limits_JNI_OnUnLoad(env);
}
if (errorsOnLoadCalled == 1) {
netty_unix_errors_JNI_OnUnLoad(env);
}
if (filedescriptorOnLoadCalled == 1) {
netty_unix_filedescriptor_JNI_OnUnLoad(env);
}
if (socketOnLoadCalled == 1) {
netty_unix_socket_JNI_OnUnLoad(env);
}
if (bufferOnLoadCalled == 1) {
netty_unix_buffer_JNI_OnUnLoad(env);
}
if (linuxsocketOnLoadCalled == 1) {
netty_epoll_linuxsocket_JNI_OnUnLoad(env);
}
packetAddrFieldId = NULL;
packetAddrLenFieldId = NULL;
packetScopeIdFieldId = NULL;
packetPortFieldId = NULL;
packetMemoryAddressFieldId = NULL;
packetCountFieldId = NULL;
return JNI_ERR;
} }
static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) { static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) {
@ -717,11 +698,7 @@ static jint JNI_OnLoad_netty_transport_native_epoll0(JavaVM* vm, void* reserved)
#endif /* NETTY_BUILD_STATIC */ #endif /* NETTY_BUILD_STATIC */
jint ret = netty_epoll_native_JNI_OnLoad(env, packagePrefix); jint ret = netty_epoll_native_JNI_OnLoad(env, packagePrefix);
if (packagePrefix != NULL) { free(packagePrefix);
free(packagePrefix);
packagePrefix = NULL;
}
return ret; return ret;
} }

View File

@ -109,8 +109,19 @@ static jobjectArray netty_kqueue_bsdsocket_getAcceptFilter(JNIEnv* env, jclass c
return NULL; return NULL;
} }
jobjectArray resultArray = (*env)->NewObjectArray(env, 2, stringClass, NULL); jobjectArray resultArray = (*env)->NewObjectArray(env, 2, stringClass, NULL);
(*env)->SetObjectArrayElement(env, resultArray, 0, (*env)->NewStringUTF(env, &af.af_name[0])); if (resultArray == NULL) {
(*env)->SetObjectArrayElement(env, resultArray, 1, (*env)->NewStringUTF(env, &af.af_arg[0])); return NULL;
}
jstring name = (*env)->NewStringUTF(env, &af.af_name[0]);
if (name == NULL) {
return NULL;
}
jstring arg = (*env)->NewStringUTF(env, &af.af_arg[0]);
if (arg == NULL) {
return NULL;
}
(*env)->SetObjectArrayElement(env, resultArray, 0, name);
(*env)->SetObjectArrayElement(env, resultArray, 1, arg);
return resultArray; return resultArray;
#else // No know replacement on MacOS #else // No know replacement on MacOS
// Don't throw here because this is used when getting a list of all options. // Don't throw here because this is used when getting a list of all options.
@ -151,11 +162,15 @@ static jobject netty_kqueue_bsdsocket_getPeerCredentials(JNIEnv *env, jclass cla
} }
jintArray gids = NULL; jintArray gids = NULL;
if (credentials.cr_ngroups > 1) { if (credentials.cr_ngroups > 1) {
gids = (*env)->NewIntArray(env, credentials.cr_ngroups); if ((gids = (*env)->NewIntArray(env, credentials.cr_ngroups)) == NULL) {
return NULL;
}
(*env)->SetIntArrayRegion(env, gids, 0, credentials.cr_ngroups, (jint*) credentials.cr_groups); (*env)->SetIntArrayRegion(env, gids, 0, credentials.cr_ngroups, (jint*) credentials.cr_groups);
} else { } else {
// It has been observed on MacOS that cr_ngroups may not be set, but the cr_gid field is set. // It has been observed on MacOS that cr_ngroups may not be set, but the cr_gid field is set.
gids = (*env)->NewIntArray(env, 1); if ((gids = (*env)->NewIntArray(env, 1)) == NULL) {
return NULL;
}
(*env)->SetIntArrayRegion(env, gids, 0, 1, (jint*) &credentials.cr_gid); (*env)->SetIntArrayRegion(env, gids, 0, 1, (jint*) &credentials.cr_gid);
} }
@ -189,130 +204,87 @@ static jint dynamicMethodsTableSize() {
} }
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize()); char* dynamicTypeName = NULL;
size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize();
JNINativeMethod* dynamicMethods = malloc(size);
if (dynamicMethods == NULL) {
return NULL;
}
memset(dynamicMethods, 0, size);
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table)); memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J");
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size]; JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
NETTY_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J", dynamicTypeName, error);
NETTY_PREPEND("(IL", dynamicTypeName, dynamicMethod->signature, error);
dynamicMethod->name = "sendFile"; dynamicMethod->name = "sendFile";
dynamicMethod->signature = netty_unix_util_prepend("(IL", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_kqueue_bsdsocket_sendFile; dynamicMethod->fnPtr = (void *) netty_kqueue_bsdsocket_sendFile;
free(dynamicTypeName); netty_unix_util_free_dynamic_name(&dynamicTypeName);
++dynamicMethod; ++dynamicMethod;
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials;"); NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials;", dynamicTypeName, error);
NETTY_PREPEND("(I)L", dynamicTypeName, dynamicMethod->signature, error);
dynamicMethod->name = "getPeerCredentials"; dynamicMethod->name = "getPeerCredentials";
dynamicMethod->signature = netty_unix_util_prepend("(I)L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_kqueue_bsdsocket_getPeerCredentials; dynamicMethod->fnPtr = (void *) netty_kqueue_bsdsocket_getPeerCredentials;
free(dynamicTypeName); netty_unix_util_free_dynamic_name(&dynamicTypeName);
return dynamicMethods; return dynamicMethods;
error:
free(dynamicTypeName);
netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
return NULL;
} }
static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) {
jint fullMethodTableSize = dynamicMethodsTableSize();
jint i = fixed_method_table_size;
for (; i < fullMethodTableSize; ++i) {
free(dynamicMethods[i].signature);
}
free(dynamicMethods);
}
// JNI Method Registration Table End // JNI Method Registration Table End
jint netty_kqueue_bsdsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { jint netty_kqueue_bsdsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
int ret = JNI_ERR;
char* nettyClassName = NULL;
jclass fileRegionCls = NULL;
jclass fileChannelCls = NULL;
jclass fileDescriptorCls = NULL;
// Register the methods which are not referenced by static member variables // Register the methods which are not referenced by static member variables
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
if (dynamicMethods == NULL) {
goto done;
}
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/kqueue/BsdSocket", "io/netty/channel/kqueue/BsdSocket",
dynamicMethods, dynamicMethods,
dynamicMethodsTableSize()) != 0) { dynamicMethodsTableSize()) != 0) {
freeDynamicMethodsTable(dynamicMethods); goto done;
return JNI_ERR;
} }
freeDynamicMethodsTable(dynamicMethods);
dynamicMethods = NULL;
// Initialize this module // Initialize this module
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion"); NETTY_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion", nettyClassName, done);
jclass fileRegionCls = (*env)->FindClass(env, nettyClassName); NETTY_FIND_CLASS(env, fileRegionCls, nettyClassName, done);
netty_unix_util_free_dynamic_name(&nettyClassName);
NETTY_GET_FIELD(env, fileRegionCls, fileChannelFieldId, "file", "Ljava/nio/channels/FileChannel;", done);
NETTY_GET_FIELD(env, fileRegionCls, transferredFieldId, "transferred", "J", done);
NETTY_FIND_CLASS(env, fileChannelCls, "sun/nio/ch/FileChannelImpl", done);
NETTY_GET_FIELD(env, fileChannelCls, fileDescriptorFieldId, "fd", "Ljava/io/FileDescriptor;", done);
NETTY_FIND_CLASS(env, fileDescriptorCls, "java/io/FileDescriptor", done);
NETTY_GET_FIELD(env, fileDescriptorCls, fdFieldId, "fd", "I", done);
NETTY_LOAD_CLASS(env, stringClass, "java/lang/String", done);
NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials", nettyClassName, done);
NETTY_LOAD_CLASS(env, peerCredentialsClass, nettyClassName, done);
netty_unix_util_free_dynamic_name(&nettyClassName);
NETTY_GET_METHOD(env, peerCredentialsClass, peerCredentialsMethodId, "<init>", "(II[I)V", done);
ret = NETTY_JNI_VERSION;
done:
netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
free(nettyClassName); free(nettyClassName);
nettyClassName = NULL;
if (fileRegionCls == NULL) {
return JNI_ERR;
}
fileChannelFieldId = (*env)->GetFieldID(env, fileRegionCls, "file", "Ljava/nio/channels/FileChannel;");
if (fileChannelFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.file");
return JNI_ERR;
}
transferredFieldId = (*env)->GetFieldID(env, fileRegionCls, "transferred", "J");
if (transferredFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.transferred");
return JNI_ERR;
}
jclass fileChannelCls = (*env)->FindClass(env, "sun/nio/ch/FileChannelImpl"); return ret;
if (fileChannelCls == NULL) {
// pending exception...
return JNI_ERR;
}
fileDescriptorFieldId = (*env)->GetFieldID(env, fileChannelCls, "fd", "Ljava/io/FileDescriptor;");
if (fileDescriptorFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileChannelImpl.fd");
return JNI_ERR;
}
jclass fileDescriptorCls = (*env)->FindClass(env, "java/io/FileDescriptor");
if (fileDescriptorCls == NULL) {
// pending exception...
return JNI_ERR;
}
fdFieldId = (*env)->GetFieldID(env, fileDescriptorCls, "fd", "I");
if (fdFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileDescriptor.fd");
return JNI_ERR;
}
jclass stringCls = (*env)->FindClass(env, "java/lang/String");
if (stringCls == NULL) {
// pending exception...
return JNI_ERR;
}
if ((stringClass = (*env)->NewGlobalRef(env, stringCls)) == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials");
jclass localPeerCredsClass = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (localPeerCredsClass == NULL) {
// pending exception...
return JNI_ERR;
}
peerCredentialsClass = (jclass) (*env)->NewGlobalRef(env, localPeerCredsClass);
if (peerCredentialsClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
peerCredentialsMethodId = (*env)->GetMethodID(env, peerCredentialsClass, "<init>", "(II[I)V");
if (peerCredentialsMethodId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: PeerCredentials.<init>(int, int, int[])");
return JNI_ERR;
}
return NETTY_JNI_VERSION;
} }
void netty_kqueue_bsdsocket_JNI_OnUnLoad(JNIEnv* env) { void netty_kqueue_bsdsocket_JNI_OnUnLoad(JNIEnv* env) {
if (peerCredentialsClass != NULL) { NETTY_UNLOAD_CLASS(env, peerCredentialsClass);
(*env)->DeleteGlobalRef(env, peerCredentialsClass); NETTY_UNLOAD_CLASS(env, stringClass);
peerCredentialsClass = NULL;
}
if (stringClass != NULL) {
(*env)->DeleteGlobalRef(env, stringClass);
stringClass = NULL;
}
} }

View File

@ -388,10 +388,8 @@ static jint JNI_OnLoad_netty_transport_native_kqueue0(JavaVM* vm, void* reserved
#endif /* NETTY_BUILD_STATIC */ #endif /* NETTY_BUILD_STATIC */
jint ret = netty_kqueue_native_JNI_OnLoad(env, packagePrefix); jint ret = netty_kqueue_native_JNI_OnLoad(env, packagePrefix);
if (packagePrefix != NULL) { // It's safe to call free(...) with a NULL argument as well.
free(packagePrefix); free(packagePrefix);
packagePrefix = NULL;
}
return ret; return ret;
} }

View File

@ -21,6 +21,7 @@
#include "netty_unix_jni.h" #include "netty_unix_jni.h"
#include "netty_unix_util.h" #include "netty_unix_util.h"
static jclass oomErrorClass = NULL;
static jclass runtimeExceptionClass = NULL; static jclass runtimeExceptionClass = NULL;
static jclass channelExceptionClass = NULL; static jclass channelExceptionClass = NULL;
static jclass ioExceptionClass = NULL; static jclass ioExceptionClass = NULL;
@ -43,12 +44,18 @@ void netty_unix_errors_throwRuntimeException(JNIEnv* env, char* message) {
void netty_unix_errors_throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { void netty_unix_errors_throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
char* allocatedMessage = exceptionMessage(message, errorNumber); char* allocatedMessage = exceptionMessage(message, errorNumber);
if (allocatedMessage == NULL) {
return;
}
(*env)->ThrowNew(env, runtimeExceptionClass, allocatedMessage); (*env)->ThrowNew(env, runtimeExceptionClass, allocatedMessage);
free(allocatedMessage); free(allocatedMessage);
} }
void netty_unix_errors_throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { void netty_unix_errors_throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
char* allocatedMessage = exceptionMessage(message, errorNumber); char* allocatedMessage = exceptionMessage(message, errorNumber);
if (allocatedMessage == NULL) {
return;
}
(*env)->ThrowNew(env, channelExceptionClass, allocatedMessage); (*env)->ThrowNew(env, channelExceptionClass, allocatedMessage);
free(allocatedMessage); free(allocatedMessage);
} }
@ -63,18 +70,23 @@ void netty_unix_errors_throwPortUnreachableException(JNIEnv* env, char* message)
void netty_unix_errors_throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { void netty_unix_errors_throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
char* allocatedMessage = exceptionMessage(message, errorNumber); char* allocatedMessage = exceptionMessage(message, errorNumber);
if (allocatedMessage == NULL) {
return;
}
(*env)->ThrowNew(env, ioExceptionClass, allocatedMessage); (*env)->ThrowNew(env, ioExceptionClass, allocatedMessage);
free(allocatedMessage); free(allocatedMessage);
} }
void netty_unix_errors_throwClosedChannelException(JNIEnv* env) { void netty_unix_errors_throwClosedChannelException(JNIEnv* env) {
jobject exception = (*env)->NewObject(env, closedChannelExceptionClass, closedChannelExceptionMethodId); jobject exception = (*env)->NewObject(env, closedChannelExceptionClass, closedChannelExceptionMethodId);
if (exception == NULL) {
return;
}
(*env)->Throw(env, exception); (*env)->Throw(env, exception);
} }
void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env) { void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env) {
jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); (*env)->ThrowNew(env, oomErrorClass, "");
(*env)->ThrowNew(env, exceptionClass, "");
} }
// JNI Registered Methods Begin // JNI Registered Methods Begin
@ -151,6 +163,7 @@ static const jint statically_referenced_fixed_method_table_size = sizeof(statica
// JNI Method Registration Table End // JNI Method Registration Table End
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
char* nettyClassName = NULL;
// We must register the statically referenced methods first! // We must register the statically referenced methods first!
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
@ -160,98 +173,33 @@ jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
return JNI_ERR; return JNI_ERR;
} }
jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException"); NETTY_LOAD_CLASS(env, oomErrorClass, "java/lang/OutOfMemoryError", error);
if (localRuntimeExceptionClass == NULL) {
// pending exception...
return JNI_ERR;
}
runtimeExceptionClass = (jclass) (*env)->NewGlobalRef(env, localRuntimeExceptionClass);
if (runtimeExceptionClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/ChannelException"); NETTY_LOAD_CLASS(env, runtimeExceptionClass, "java/lang/RuntimeException", error);
jclass localChannelExceptionClass = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (localChannelExceptionClass == NULL) {
// pending exception...
return JNI_ERR;
}
channelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localChannelExceptionClass);
if (channelExceptionClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
// cache classes that are used within other jni methods for performance reasons NETTY_PREPEND(packagePrefix, "io/netty/channel/ChannelException", nettyClassName, error);
jclass localClosedChannelExceptionClass = (*env)->FindClass(env, "java/nio/channels/ClosedChannelException"); NETTY_LOAD_CLASS(env, channelExceptionClass, nettyClassName, error);
if (localClosedChannelExceptionClass == NULL) { netty_unix_util_free_dynamic_name(&nettyClassName);
// pending exception...
return JNI_ERR;
}
closedChannelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localClosedChannelExceptionClass);
if (closedChannelExceptionClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
closedChannelExceptionMethodId = (*env)->GetMethodID(env, closedChannelExceptionClass, "<init>", "()V");
if (closedChannelExceptionMethodId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ClosedChannelException.<init>()");
return JNI_ERR;
}
jclass localIoExceptionClass = (*env)->FindClass(env, "java/io/IOException"); NETTY_LOAD_CLASS(env, closedChannelExceptionClass, "java/nio/channels/ClosedChannelException", error);
if (localIoExceptionClass == NULL) { NETTY_GET_METHOD(env, closedChannelExceptionClass, closedChannelExceptionMethodId, "<init>", "()V", error);
// pending exception...
return JNI_ERR;
}
ioExceptionClass = (jclass) (*env)->NewGlobalRef(env, localIoExceptionClass);
if (ioExceptionClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
jclass localPortUnreachableExceptionClass = (*env)->FindClass(env, "java/net/PortUnreachableException"); NETTY_LOAD_CLASS(env, ioExceptionClass, "java/io/IOException", error);
if (localPortUnreachableExceptionClass == NULL) {
// pending exception... NETTY_LOAD_CLASS(env, portUnreachableExceptionClass, "java/net/PortUnreachableException", error);
return JNI_ERR;
}
portUnreachableExceptionClass = (jclass) (*env)->NewGlobalRef(env, localPortUnreachableExceptionClass);
if (portUnreachableExceptionClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
return NETTY_JNI_VERSION; return NETTY_JNI_VERSION;
error:
free(nettyClassName);
return JNI_ERR;
} }
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) { void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) {
// delete global references so the GC can collect them // delete global references so the GC can collect them
if (runtimeExceptionClass != NULL) { NETTY_UNLOAD_CLASS(env, oomErrorClass);
(*env)->DeleteGlobalRef(env, runtimeExceptionClass); NETTY_UNLOAD_CLASS(env, runtimeExceptionClass);
runtimeExceptionClass = NULL; NETTY_UNLOAD_CLASS(env, channelExceptionClass);
} NETTY_UNLOAD_CLASS(env, ioExceptionClass);
if (channelExceptionClass != NULL) { NETTY_UNLOAD_CLASS(env, portUnreachableExceptionClass);
(*env)->DeleteGlobalRef(env, channelExceptionClass); NETTY_UNLOAD_CLASS(env, closedChannelExceptionClass);
channelExceptionClass = NULL;
}
if (ioExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, ioExceptionClass);
ioExceptionClass = NULL;
}
if (portUnreachableExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, portUnreachableExceptionClass);
portUnreachableExceptionClass = NULL;
}
if (closedChannelExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, closedChannelExceptionClass);
closedChannelExceptionClass = NULL;
}
} }

View File

@ -276,65 +276,41 @@ static const jint method_table_size = sizeof(method_table) / sizeof(method_table
// JNI Method Registration Table End // JNI Method Registration Table End
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
int ret = JNI_ERR;
void* mem = NULL;
if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/unix/FileDescriptor", method_table, method_table_size) != 0) { if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/unix/FileDescriptor", method_table, method_table_size) != 0) {
return JNI_ERR; goto done;
} }
void* mem = malloc(1); if ((mem = malloc(1)) == NULL) {
if (mem == NULL) { goto done;
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
} }
jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1); jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1);
if (directBuffer == NULL) { if (directBuffer == NULL) {
free(mem); goto done;
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
} }
if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) { if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) {
free(mem); goto done;
netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address");
return JNI_ERR;
} }
jclass cls = (*env)->GetObjectClass(env, directBuffer); jclass cls = (*env)->GetObjectClass(env, directBuffer);
if (cls == NULL) {
goto done;
}
// Get the method id for Buffer.position() and Buffer.limit(). These are used as fallback if // Get the method id for Buffer.position() and Buffer.limit(). These are used as fallback if
// it is not possible to obtain the position and limit using the fields directly. // it is not possible to obtain the position and limit using the fields directly.
posId = (*env)->GetMethodID(env, cls, "position", "()I"); NETTY_GET_METHOD(env, cls, posId, "position", "()I", done);
if (posId == NULL) { NETTY_GET_METHOD(env, cls, limitId, "limit", "()I", done);
free(mem);
// position method was not found.. something is wrong so bail out
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ByteBuffer.position()");
return JNI_ERR;
}
limitId = (*env)->GetMethodID(env, cls, "limit", "()I");
if (limitId == NULL) {
free(mem);
// limit method was not found.. something is wrong so bail out
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ByteBuffer.limit()");
return JNI_ERR;
}
// Try to get the ids of the position and limit fields. We later then check if we was able // Try to get the ids of the position and limit fields. We later then check if we was able
// to find them and if so use them get the position and limit of the buffer. This is // to find them and if so use them get the position and limit of the buffer. This is
// much faster then call back into java via (*env)->CallIntMethod(...). // much faster then call back into java via (*env)->CallIntMethod(...).
posFieldId = (*env)->GetFieldID(env, cls, "position", "I"); NETTY_TRY_GET_FIELD(env, cls, posFieldId, "position", "I");
if (posFieldId == NULL) { NETTY_TRY_GET_FIELD(env, cls, limitFieldId, "limit", "I");
// this is ok as we can still use the method so just clear the exception
(*env)->ExceptionClear(env);
}
limitFieldId = (*env)->GetFieldID(env, cls, "limit", "I");
if (limitFieldId == NULL) {
// this is ok as we can still use the method so just clear the exception
(*env)->ExceptionClear(env);
}
ret = NETTY_JNI_VERSION;
done:
free(mem); free(mem);
return NETTY_JNI_VERSION; return ret;
} }
void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env) { } void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env) { }

View File

@ -85,7 +85,9 @@ static jobject createDatagramSocketAddress(JNIEnv* env, const struct sockaddr_st
int scopeId; int scopeId;
int ipLength = netty_unix_socket_ipAddressLength(addr); int ipLength = netty_unix_socket_ipAddressLength(addr);
jbyteArray addressBytes = (*env)->NewByteArray(env, ipLength); jbyteArray addressBytes = (*env)->NewByteArray(env, ipLength);
if (addressBytes == NULL) {
return NULL;
}
if (addr->ss_family == AF_INET) { if (addr->ss_family == AF_INET) {
struct sockaddr_in* s = (struct sockaddr_in*) addr; struct sockaddr_in* s = (struct sockaddr_in*) addr;
port = ntohs(s->sin_port); port = ntohs(s->sin_port);
@ -174,7 +176,9 @@ static void initInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storag
static jbyteArray createInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr) { static jbyteArray createInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr) {
jsize len = addressLength(addr); jsize len = addressLength(addr);
jbyteArray bArray = (*env)->NewByteArray(env, len); jbyteArray bArray = (*env)->NewByteArray(env, len);
if (bArray == NULL) {
return NULL;
}
initInetSocketAddressArray(env, addr, bArray, 0, len); initInetSocketAddressArray(env, addr, bArray, 0, len);
return bArray; return bArray;
} }
@ -1009,128 +1013,87 @@ static jint dynamicMethodsTableSize() {
} }
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize()); char* dynamicTypeName = NULL;
size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize();
JNINativeMethod* dynamicMethods = malloc(size);
if (dynamicMethods == NULL) {
return NULL;
}
memset(dynamicMethods, 0, size);
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table)); memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;");
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size]; JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;", dynamicTypeName, error);
NETTY_PREPEND("(ILjava/nio/ByteBuffer;II)L", dynamicTypeName, dynamicMethod->signature, error);
dynamicMethod->name = "recvFrom"; dynamicMethod->name = "recvFrom";
dynamicMethod->signature = netty_unix_util_prepend("(ILjava/nio/ByteBuffer;II)L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFrom; dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFrom;
free(dynamicTypeName); netty_unix_util_free_dynamic_name(&dynamicTypeName);
++dynamicMethod; ++dynamicMethod;
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;"); NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;", dynamicTypeName, error);
NETTY_PREPEND("(IJII)L", dynamicTypeName, dynamicMethod->signature, error);
dynamicMethod->name = "recvFromAddress"; dynamicMethod->name = "recvFromAddress";
dynamicMethod->signature = netty_unix_util_prepend("(IJII)L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFromAddress; dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFromAddress;
free(dynamicTypeName); netty_unix_util_free_dynamic_name(&dynamicTypeName);
return dynamicMethods; return dynamicMethods;
error:
free(dynamicTypeName);
netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
return NULL;
} }
static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) {
jint fullMethodTableSize = dynamicMethodsTableSize();
jint i = fixed_method_table_size;
for (; i < fullMethodTableSize; ++i) {
free(dynamicMethods[i].signature);
}
free(dynamicMethods);
}
// JNI Method Registration Table End // JNI Method Registration Table End
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
int ret = JNI_ERR;
char* nettyClassName = NULL;
void* mem = NULL;
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
if (dynamicMethods == NULL) {
goto done;
}
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/unix/Socket", "io/netty/channel/unix/Socket",
dynamicMethods, dynamicMethods,
dynamicMethodsTableSize()) != 0) { dynamicMethodsTableSize()) != 0) {
freeDynamicMethodsTable(dynamicMethods); goto done;
return JNI_ERR;
}
freeDynamicMethodsTable(dynamicMethods);
dynamicMethods = NULL;
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress");
jclass localDatagramSocketAddressClass = (*env)->FindClass(env, nettyClassName);
if (localDatagramSocketAddressClass == NULL) {
free(nettyClassName);
nettyClassName = NULL;
// pending exception...
return JNI_ERR;
}
datagramSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localDatagramSocketAddressClass);
if (datagramSocketAddressClass == NULL) {
free(nettyClassName);
nettyClassName = NULL;
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
} }
NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress", nettyClassName, done);
NETTY_LOAD_CLASS(env, datagramSocketAddressClass, nettyClassName, done);
// Respect shading... // Respect shading...
char parameters[1024] = {0}; char parameters[1024] = {0};
snprintf(parameters, sizeof(parameters), "([BIIIL%s;)V", nettyClassName); snprintf(parameters, sizeof(parameters), "([BIIIL%s;)V", nettyClassName);
netty_unix_util_free_dynamic_name(&nettyClassName);
NETTY_GET_METHOD(env, datagramSocketAddressClass, datagramSocketAddrMethodId, "<init>", parameters, done);
datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "<init>", parameters); NETTY_LOAD_CLASS(env, inetSocketAddressClass, "java/net/InetSocketAddress", done);
if (datagramSocketAddrMethodId == NULL) { NETTY_GET_METHOD(env, inetSocketAddressClass, inetSocketAddrMethodId, "<init>", "(Ljava/lang/String;I)V", done);
char msg[1024] = {0};
snprintf(msg, sizeof(msg), "failed to get method ID: %s.<init>(byte[], int, int, int, %s)", nettyClassName, nettyClassName); if ((mem = malloc(1)) == NULL) {
free(nettyClassName); goto done;
nettyClassName = NULL;
netty_unix_errors_throwRuntimeException(env, msg);
return JNI_ERR;
} }
free(nettyClassName);
nettyClassName = NULL;
jclass localInetSocketAddressClass = (*env)->FindClass(env, "java/net/InetSocketAddress");
if (localInetSocketAddressClass == NULL) {
// pending exception...
return JNI_ERR;
}
inetSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localInetSocketAddressClass);
if (inetSocketAddressClass == NULL) {
// out-of-memory!
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
inetSocketAddrMethodId = (*env)->GetMethodID(env, inetSocketAddressClass, "<init>", "(Ljava/lang/String;I)V");
if (inetSocketAddrMethodId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.<init>(String, int)");
return JNI_ERR;
}
void* mem = malloc(1);
if (mem == NULL) {
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
}
jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1); jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1);
if (directBuffer == NULL) { if (directBuffer == NULL) {
free(mem); goto done;
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
} }
if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) { if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) {
free(mem); goto done;
netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address");
return JNI_ERR;
} }
free(mem);
return NETTY_JNI_VERSION; ret = NETTY_JNI_VERSION;
done:
netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
free(nettyClassName);
free(mem);
return ret;
} }
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) { void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
if (datagramSocketAddressClass != NULL) { NETTY_UNLOAD_CLASS(env, datagramSocketAddressClass);
(*env)->DeleteGlobalRef(env, datagramSocketAddressClass); NETTY_UNLOAD_CLASS(env, inetSocketAddressClass);
datagramSocketAddressClass = NULL;
}
if (inetSocketAddressClass != NULL) {
(*env)->DeleteGlobalRef(env, inetSocketAddressClass);
inetSocketAddressClass = NULL;
}
} }

View File

@ -29,12 +29,17 @@ static const uint64_t NETTY_BILLION = 1000000000L;
#endif /* NETTY_USE_MACH_INSTEAD_OF_CLOCK */ #endif /* NETTY_USE_MACH_INSTEAD_OF_CLOCK */
char* netty_unix_util_prepend(const char* prefix, const char* str) { char* netty_unix_util_prepend(const char* prefix, const char* str) {
char* result = NULL;
if (prefix == NULL) { if (prefix == NULL) {
char* result = (char*) malloc(sizeof(char) * (strlen(str) + 1)); if ((result = (char*) malloc(sizeof(char) * (strlen(str) + 1))) == NULL) {
return NULL;
}
strcpy(result, str); strcpy(result, str);
return result; return result;
} }
char* result = (char*) malloc(sizeof(char) * (strlen(prefix) + strlen(str) + 1)); if ((result = (char*) malloc(sizeof(char) * (strlen(prefix) + strlen(str) + 1))) == NULL) {
return NULL;
}
strcpy(result, prefix); strcpy(result, prefix);
strcat(result, str); strcat(result, str);
return result; return result;
@ -82,7 +87,10 @@ char* netty_unix_util_parse_package_prefix(const char* libraryPathName, const ch
// packagePrefix length is > 0 // packagePrefix length is > 0
// Make a copy so we can modify the value without impacting libraryPathName. // Make a copy so we can modify the value without impacting libraryPathName.
size_t packagePrefixLen = packageNameEnd - packagePrefix; size_t packagePrefixLen = packageNameEnd - packagePrefix;
packagePrefix = strndup(packagePrefix, packagePrefixLen); if ((packagePrefix = strndup(packagePrefix, packagePrefixLen)) == NULL) {
*status = JNI_ERR;
return NULL;
}
// Make sure the packagePrefix is in the correct format for the JNI functions it will be used with. // Make sure the packagePrefix is in the correct format for the JNI functions it will be used with.
char* temp = packagePrefix; char* temp = packagePrefix;
packageNameEnd = packagePrefix + packagePrefixLen; packageNameEnd = packagePrefix + packagePrefixLen;
@ -95,7 +103,9 @@ char* netty_unix_util_parse_package_prefix(const char* libraryPathName, const ch
// Make sure packagePrefix is terminated with the '/' JNI package separator. // Make sure packagePrefix is terminated with the '/' JNI package separator.
if(*(--temp) != '/') { if(*(--temp) != '/') {
temp = packagePrefix; temp = packagePrefix;
packagePrefix = netty_unix_util_prepend(packagePrefix, "/"); if ((packagePrefix = netty_unix_util_prepend(packagePrefix, "/")) == NULL) {
*status = JNI_ERR;
}
free(temp); free(temp);
} }
return packagePrefix; return packagePrefix;
@ -185,13 +195,34 @@ jboolean netty_unix_util_initialize_wait_clock(clockid_t* clockId) {
} }
jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods) { jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods) {
char* nettyClassName = netty_unix_util_prepend(packagePrefix, className); char* nettyClassName = NULL;
int ret = JNI_ERR;
NETTY_PREPEND(packagePrefix, className, nettyClassName, done);
jclass nativeCls = (*env)->FindClass(env, nettyClassName); jclass nativeCls = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (nativeCls == NULL) { if (nativeCls == NULL) {
return JNI_ERR; goto done;
} }
return (*env)->RegisterNatives(env, nativeCls, methods, numMethods); ret = (*env)->RegisterNatives(env, nativeCls, methods, numMethods);
done:
free(nettyClassName);
return ret;
}
void netty_unix_util_free_dynamic_methods_table(JNINativeMethod* dynamicMethods, jint fixedMethodTableSize, jint fullMethodTableSize) {
if (dynamicMethods != NULL) {
jint i = fixedMethodTableSize;
for (; i < fullMethodTableSize; ++i) {
free(dynamicMethods[i].signature);
}
free(dynamicMethods);
}
}
void netty_unix_util_free_dynamic_name(char** dynamicName) {
if (dynamicName != NULL && *dynamicName != NULL) {
free(*dynamicName);
*dynamicName = NULL;
}
} }

View File

@ -36,6 +36,72 @@ typedef int clockid_t;
#endif /* __MACH__ */ #endif /* __MACH__ */
#define NETTY_BEGIN_MACRO if (1) {
#define NETTY_END_MACRO } else (void)(0)
#define NETTY_FIND_CLASS(E, C, N, R) \
NETTY_BEGIN_MACRO \
C = (*(E))->FindClass((E), N); \
if (C == NULL) { \
(*(E))->ExceptionClear((E)); \
goto R; \
} \
NETTY_END_MACRO
#define NETTY_LOAD_CLASS(E, C, N, R) \
NETTY_BEGIN_MACRO \
jclass _##C = (*(E))->FindClass((E), N); \
if (_##C == NULL) { \
(*(E))->ExceptionClear((E)); \
goto R; \
} \
C = (*(E))->NewGlobalRef((E), _##C); \
(*(E))->DeleteLocalRef((E), _##C); \
if (C == NULL) { \
goto R; \
} \
NETTY_END_MACRO
#define NETTY_UNLOAD_CLASS(E, C) \
NETTY_BEGIN_MACRO \
if (C != NULL) { \
(*(E))->DeleteGlobalRef((E), (C)); \
C = NULL; \
} \
NETTY_END_MACRO
#define NETTY_GET_METHOD(E, C, M, N, S, R) \
NETTY_BEGIN_MACRO \
M = (*(E))->GetMethodID((E), C, N, S); \
if (M == NULL) { \
goto R; \
} \
NETTY_END_MACRO
#define NETTY_GET_FIELD(E, C, F, N, S, R) \
NETTY_BEGIN_MACRO \
F = (*(E))->GetFieldID((E), C, N, S); \
if (F == NULL) { \
goto R; \
} \
NETTY_END_MACRO
#define NETTY_TRY_GET_FIELD(E, C, F, N, S) \
NETTY_BEGIN_MACRO \
F = (*(E))->GetFieldID((E), C, N, S); \
if (F == NULL) { \
(*(E))->ExceptionClear((E)); \
} \
NETTY_END_MACRO
#define NETTY_PREPEND(P, S, N, R) \
NETTY_BEGIN_MACRO \
if ((N = netty_unix_util_prepend(P, S)) == NULL) { \
goto R; \
} \
NETTY_END_MACRO
/** /**
* Return a new string (caller must free this string) which is equivalent to <pre>prefix + str</pre>. * Return a new string (caller must free this string) which is equivalent to <pre>prefix + str</pre>.
* *
@ -84,4 +150,7 @@ jboolean netty_unix_util_timespec_subtract_ns(struct timespec* ts, uint64_t nano
*/ */
jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods); jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods);
void netty_unix_util_free_dynamic_methods_table(JNINativeMethod* dynamicMethods, jint fixedMethodTableSize, jint fullMethodTableSize);
void netty_unix_util_free_dynamic_name(char** dynamicName);
#endif /* NETTY_UNIX_UTIL_H_ */ #endif /* NETTY_UNIX_UTIL_H_ */