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
parent e5eb94668c
commit 15eef2425a
9 changed files with 423 additions and 519 deletions

View File

@ -711,113 +711,83 @@ static jint dynamicMethodsTableSize() {
}
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));
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->signature = netty_unix_util_prepend("(I)L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_getPeerCredentials;
free(dynamicTypeName);
netty_unix_util_free_dynamic_name(&dynamicTypeName);
++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->signature = netty_unix_util_prepend("(IL", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_sendFile;
free(dynamicTypeName);
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;
}
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
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);
if (dynamicMethods == NULL) {
goto done;
}
if (netty_unix_util_register_natives(env,
packagePrefix,
"io/netty/channel/epoll/LinuxSocket",
dynamicMethods,
dynamicMethodsTableSize()) != 0) {
freeDynamicMethodsTable(dynamicMethods);
return JNI_ERR;
goto done;
}
freeDynamicMethodsTable(dynamicMethods);
dynamicMethods = NULL;
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials");
jclass localPeerCredsClass = (*env)->FindClass(env, nettyClassName);
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);
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);
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");
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;
return ret;
}
void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env) {
if (peerCredentialsClass != NULL) {
(*env)->DeleteGlobalRef(env, peerCredentialsClass);
peerCredentialsClass = NULL;
}
NETTY_UNLOAD_CLASS(env, peerCredentialsClass);
}

View File

@ -514,42 +514,48 @@ static jint dynamicMethodsTableSize() {
}
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize());
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I");
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
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);
char* dynamicTypeName = NULL;
size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize();
JNINativeMethod* dynamicMethods = malloc(size);
if (dynamicMethods == NULL) {
return NULL;
}
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
static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
int ret = JNI_ERR;
int limitsOnLoadCalled = 0;
int errorsOnLoadCalled = 0;
int filedescriptorOnLoadCalled = 0;
int socketOnLoadCalled = 0;
int bufferOnLoadCalled = 0;
int linuxsocketOnLoadCalled = 0;
char* nettyClassName = NULL;
jclass nativeDatagramPacketCls = NULL;
// We must register the statically referenced methods first!
if (netty_unix_util_register_natives(env,
@ -557,122 +563,97 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix
"io/netty/channel/epoll/NativeStaticallyReferencedJniMethods",
statically_referenced_fixed_method_table,
statically_referenced_fixed_method_table_size) != 0) {
goto error;
goto done;
}
// Register the methods which are not referenced by static member variables
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
if (dynamicMethods == NULL) {
goto done;
}
if (netty_unix_util_register_natives(env,
packagePrefix,
"io/netty/channel/epoll/Native",
dynamicMethods,
dynamicMethodsTableSize()) != 0) {
freeDynamicMethodsTable(dynamicMethods);
goto error;
goto done;
}
freeDynamicMethodsTable(dynamicMethods);
dynamicMethods = NULL;
// Load all c modules that we depend upon
if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error;
goto done;
}
limitsOnLoadCalled = 1;
if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error;
goto done;
}
errorsOnLoadCalled = 1;
if (netty_unix_filedescriptor_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error;
goto done;
}
filedescriptorOnLoadCalled = 1;
if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error;
goto done;
}
socketOnLoadCalled = 1;
if (netty_unix_buffer_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error;
goto done;
}
bufferOnLoadCalled = 1;
if (netty_epoll_linuxsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error;
goto done;
}
linuxsocketOnLoadCalled = 1;
// Initialize this module
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket");
jclass nativeDatagramPacketCls = (*env)->FindClass(env, nettyClassName);
NETTY_PREPEND(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket", nettyClassName, done);
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);
nettyClassName = NULL;
if (nativeDatagramPacketCls == NULL) {
// pending exception...
goto error;
}
packetAddrFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "addr", "[B");
if (packetAddrFieldId == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.addr");
goto error;
if (ret == JNI_ERR) {
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;
}
packetAddrLenFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "addrLen", "I");
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;
return ret;
}
static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) {
@ -715,11 +696,7 @@ static jint JNI_OnLoad_netty_transport_native_epoll0(JavaVM* vm, void* reserved)
#endif /* NETTY_BUILD_STATIC */
jint ret = netty_epoll_native_JNI_OnLoad(env, packagePrefix);
if (packagePrefix != NULL) {
free(packagePrefix);
packagePrefix = NULL;
}
free(packagePrefix);
return ret;
}

View File

@ -109,8 +109,19 @@ static jobjectArray netty_kqueue_bsdsocket_getAcceptFilter(JNIEnv* env, jclass c
return NULL;
}
jobjectArray resultArray = (*env)->NewObjectArray(env, 2, stringClass, NULL);
(*env)->SetObjectArrayElement(env, resultArray, 0, (*env)->NewStringUTF(env, &af.af_name[0]));
(*env)->SetObjectArrayElement(env, resultArray, 1, (*env)->NewStringUTF(env, &af.af_arg[0]));
if (resultArray == NULL) {
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;
#else // No know replacement on MacOS
// 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;
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);
} else {
// 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);
}
@ -189,130 +204,87 @@ static jint dynamicMethodsTableSize() {
}
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));
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J");
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->signature = netty_unix_util_prepend("(IL", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_kqueue_bsdsocket_sendFile;
free(dynamicTypeName);
netty_unix_util_free_dynamic_name(&dynamicTypeName);
++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->signature = netty_unix_util_prepend("(I)L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_kqueue_bsdsocket_getPeerCredentials;
free(dynamicTypeName);
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;
}
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
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
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
if (dynamicMethods == NULL) {
goto done;
}
if (netty_unix_util_register_natives(env,
packagePrefix,
"io/netty/channel/kqueue/BsdSocket",
dynamicMethods,
dynamicMethodsTableSize()) != 0) {
freeDynamicMethodsTable(dynamicMethods);
return JNI_ERR;
goto done;
}
freeDynamicMethodsTable(dynamicMethods);
dynamicMethods = NULL;
// Initialize this module
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion");
jclass fileRegionCls = (*env)->FindClass(env, nettyClassName);
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);
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);
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;
}
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;
return ret;
}
void netty_kqueue_bsdsocket_JNI_OnUnLoad(JNIEnv* env) {
if (peerCredentialsClass != NULL) {
(*env)->DeleteGlobalRef(env, peerCredentialsClass);
peerCredentialsClass = NULL;
}
if (stringClass != NULL) {
(*env)->DeleteGlobalRef(env, stringClass);
stringClass = NULL;
}
NETTY_UNLOAD_CLASS(env, peerCredentialsClass);
NETTY_UNLOAD_CLASS(env, stringClass);
}

View File

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

View File

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

View File

@ -276,65 +276,41 @@ static const jint method_table_size = sizeof(method_table) / sizeof(method_table
// JNI Method Registration Table End
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) {
return JNI_ERR;
goto done;
}
void* mem = malloc(1);
if (mem == NULL) {
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
if ((mem = malloc(1)) == NULL) {
goto done;
}
jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1);
if (directBuffer == NULL) {
free(mem);
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
goto done;
}
if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) {
free(mem);
netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address");
return JNI_ERR;
goto done;
}
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
// it is not possible to obtain the position and limit using the fields directly.
posId = (*env)->GetMethodID(env, cls, "position", "()I");
if (posId == NULL) {
free(mem);
NETTY_GET_METHOD(env, cls, posId, "position", "()I", done);
NETTY_GET_METHOD(env, cls, limitId, "limit", "()I", done);
// 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
// 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(...).
posFieldId = (*env)->GetFieldID(env, cls, "position", "I");
if (posFieldId == NULL) {
// 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);
}
NETTY_TRY_GET_FIELD(env, cls, posFieldId, "position", "I");
NETTY_TRY_GET_FIELD(env, cls, limitFieldId, "limit", "I");
ret = NETTY_JNI_VERSION;
done:
free(mem);
return NETTY_JNI_VERSION;
return ret;
}
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 ipLength = netty_unix_socket_ipAddressLength(addr);
jbyteArray addressBytes = (*env)->NewByteArray(env, ipLength);
if (addressBytes == NULL) {
return NULL;
}
if (addr->ss_family == AF_INET) {
struct sockaddr_in* s = (struct sockaddr_in*) addr;
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) {
jsize len = addressLength(addr);
jbyteArray bArray = (*env)->NewByteArray(env, len);
if (bArray == NULL) {
return NULL;
}
initInetSocketAddressArray(env, addr, bArray, 0, len);
return bArray;
}
@ -1009,128 +1013,87 @@ static jint dynamicMethodsTableSize() {
}
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));
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;");
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->signature = netty_unix_util_prepend("(ILjava/nio/ByteBuffer;II)L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFrom;
free(dynamicTypeName);
netty_unix_util_free_dynamic_name(&dynamicTypeName);
++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->signature = netty_unix_util_prepend("(IJII)L", dynamicTypeName);
dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFromAddress;
free(dynamicTypeName);
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;
}
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
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);
if (dynamicMethods == NULL) {
goto done;
}
if (netty_unix_util_register_natives(env,
packagePrefix,
"io/netty/channel/unix/Socket",
dynamicMethods,
dynamicMethodsTableSize()) != 0) {
freeDynamicMethodsTable(dynamicMethods);
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;
goto done;
}
NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress", nettyClassName, done);
NETTY_LOAD_CLASS(env, datagramSocketAddressClass, nettyClassName, done);
// Respect shading...
char parameters[1024] = {0};
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);
if (datagramSocketAddrMethodId == NULL) {
char msg[1024] = {0};
snprintf(msg, sizeof(msg), "failed to get method ID: %s.<init>(byte[], int, int, int, %s)", nettyClassName, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
netty_unix_errors_throwRuntimeException(env, msg);
return JNI_ERR;
NETTY_LOAD_CLASS(env, inetSocketAddressClass, "java/net/InetSocketAddress", done);
NETTY_GET_METHOD(env, inetSocketAddressClass, inetSocketAddrMethodId, "<init>", "(Ljava/lang/String;I)V", done);
if ((mem = malloc(1)) == NULL) {
goto done;
}
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);
if (directBuffer == NULL) {
free(mem);
netty_unix_errors_throwOutOfMemoryError(env);
return JNI_ERR;
goto done;
}
if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) {
free(mem);
netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address");
return JNI_ERR;
goto done;
}
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) {
if (datagramSocketAddressClass != NULL) {
(*env)->DeleteGlobalRef(env, datagramSocketAddressClass);
datagramSocketAddressClass = NULL;
}
if (inetSocketAddressClass != NULL) {
(*env)->DeleteGlobalRef(env, inetSocketAddressClass);
inetSocketAddressClass = NULL;
}
NETTY_UNLOAD_CLASS(env, datagramSocketAddressClass);
NETTY_UNLOAD_CLASS(env, inetSocketAddressClass);
}

View File

@ -29,12 +29,17 @@ static const uint64_t NETTY_BILLION = 1000000000L;
#endif /* NETTY_USE_MACH_INSTEAD_OF_CLOCK */
char* netty_unix_util_prepend(const char* prefix, const char* str) {
char* result = 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);
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);
strcat(result, str);
return result;
@ -82,7 +87,10 @@ char* netty_unix_util_parse_package_prefix(const char* libraryPathName, const ch
// packagePrefix length is > 0
// Make a copy so we can modify the value without impacting libraryPathName.
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.
char* temp = packagePrefix;
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.
if(*(--temp) != '/') {
temp = packagePrefix;
packagePrefix = netty_unix_util_prepend(packagePrefix, "/");
if ((packagePrefix = netty_unix_util_prepend(packagePrefix, "/")) == NULL) {
*status = JNI_ERR;
}
free(temp);
}
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) {
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);
free(nettyClassName);
nettyClassName = 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__ */
#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>.
*
@ -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);
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_ */