/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include "libjniutil.h"
/*
* Creates an instance of a class using the specified constructor and
* arguments. Returns a newly created object. If it returns NULL, we
* failed and have thrown an exception.
*/
jobject
makeobject(JNIEnv *env, jclass class, jmethodID ctor, ...)
{
jobject result;
va_list alist;
va_start(alist, ctor);
result = (*env)->NewObjectV(env, class, ctor, alist);
if (result == NULL)
if ((*env)->ExceptionCheck(env) == JNI_FALSE)
(*env)->FatalError(env, "Couldn't create object");
va_end(alist);
return (result);
}
/*
* Find the class specified by 'def', and optionally looks up the
* specified constructor. Stores a reference to the class and the
* method id of the constructor in the variables referenced by 'def'.
* If 'global' is true, a global reference to the class is obtained.
*/
void
load_class(JNIEnv *env, classdef_t *def, boolean_t global)
{
jclass tclass, rclass;
tclass = (*env)->FindClass(env, def->class_name);
if (tclass == 0) {
(void) fprintf(stderr, "Couldn't find class \"%s\"\n",
def->class_name);
(*env)->FatalError(env, "Couldn't find required class");
}
if (global) {
rclass = (*env)->NewGlobalRef(env, tclass);
assert(rclass != 0);
*def->class_ptr = rclass;
(*env)->DeleteLocalRef(env, tclass);
} else {
*def->class_ptr = rclass = tclass;
}
if (def->ctor_ptr) {
jmethodID rctor;
rctor = (*env)->GetMethodID(env, rclass, "<init>",
def->ctor_name);
if (rctor == 0) {
(void) fprintf(stderr,
"Couldn't find constructor for \"%s\"\n",
def->class_name);
(*env)->FatalError(env,
"Couldn't find required class constructor");
}
*def->ctor_ptr = rctor;
}
}
/*
* Construct and throw the specified exception object.
*/
void
throw(JNIEnv *env, jclass class, jmethodID ctor, ...)
{
jobject xcp;
va_list alist;
va_start(alist, ctor);
assert((*env)->ExceptionCheck(env) == JNI_FALSE);
if ((xcp = (*env)->NewObjectV(env, class, ctor, alist)) == NULL ||
(*env)->Throw(env, xcp))
(*env)->FatalError(env, "Couldn't throw exception");
va_end(alist);
}
/*
* Construct and throw the named exception using its
* (Ljava/lang/String;)V or (V)V constructor.
*/
void
vsimpleexception(JNIEnv *env, const char *classname, const char *format,
va_list alist)
{
jclass class;
jmethodID ctor;
jstring jdesc;
classdef_t strxcp = {
&class, &ctor, classname, NULL
};
assert((*env)->ExceptionCheck(env) == JNI_FALSE);
if (format != NULL) {
char buffer[1000];
(void) vsnprintf(buffer, sizeof (buffer), format, alist);
strxcp.ctor_name = "(Ljava/lang/String;)V";
load_class(env, &strxcp, B_FALSE);
jdesc = (*env)->NewStringUTF(env, buffer);
if (jdesc == NULL)
return;
throw(env, class, ctor, jdesc);
(*env)->DeleteLocalRef(env, jdesc);
} else {
strxcp.ctor_name = "(V)V";
load_class(env, &strxcp, B_FALSE);
throw(env, class, ctor);
}
}
/*
* Construct and throw the named exception using its
* (Ljava/lang/String;)V or (V)V constructor.
*/
void
simpleexception(JNIEnv *env, const char *classname, const char *format, ...)
{
va_list alist;
va_start(alist, format);
vsimpleexception(env, classname, format, alist);
va_end(alist);
}
/*
* Throws a java.io.IOException with the specified description string.
*/
void
ioexception(JNIEnv *env, const char *format, ...)
{
va_list alist;
va_start(alist, format);
vsimpleexception(env, "java/io/IOException", format, alist);
va_end(alist);
}
/*
* Throws a java.lang.IllegalArgumentException with the specified
* description string.
*/
void
iaexception(JNIEnv *env, const char *format, ...)
{
va_list alist;
va_start(alist, format);
vsimpleexception(env, "java/lang/IllegalArgumentException",
format, alist);
va_end(alist);
}
/*
* Throws a java.lang.NullPointerException with the specified
* description string.
*/
void
npexception(JNIEnv *env, const char *format, ...)
{
va_list alist;
va_start(alist, format);
vsimpleexception(env, "java/lang/NullPointerException", format, alist);
va_end(alist);
}
/*
* Throws a java.lang.NullPointerException with the specified
* description string.
*/
void
oomerror(JNIEnv *env, const char *format, ...)
{
va_list alist;
va_start(alist, format);
vsimpleexception(env, "java/lang/OutOfMemoryError", format, alist);
va_end(alist);
}
/*
* Find the field id of the specified field of type long.
*/
jfieldID
fetch_ptr_id(JNIEnv *env, jclass class, const char *name)
{
jfieldID fid;
fid = (*env)->GetFieldID(env, class, name, "J");
assert((fid != 0) || (*env)->ExceptionCheck(env));
return (fid);
}
/*
* Converts a batch of Java strings to C strings.
*/
boolean_t
cify_strings(JNIEnv *env, cstrdef_t *cstrs)
{
cstrdef_t *cur;
for (cur = cstrs; cur->cstr != NULL; cur++) {
if (cur->jstr == NULL) {
if ((cur->flags & STR_OPTIONAL) == 0) {
npexception(env, "null argument");
goto error;
}
*cur->cstr = NULL;
} else if ((*cur->cstr =
(*env)->GetStringUTFChars(env, cur->jstr, 0)) == NULL)
goto error;
}
return (B_TRUE);
error:
/*
* If we encountered an error, there is at least one non-null
* entry that points to a null value (so no need to test for
* cur->cstr != NULL).
*/
for (cur = cstrs; *cur->cstr != NULL; cur++) {
(*env)->ReleaseStringUTFChars(env, cur->jstr, *cur->cstr);
*cur->cstr = NULL;
}
return (B_FALSE);
}
/*
* Frees a batch of C strings created by cify_strings
*/
void
cify_release(JNIEnv *env, cstrdef_t *cstrs)
{
cstrdef_t *cur;
for (cur = cstrs; cur->cstr != NULL; cur++) {
if (*cur->cstr != NULL)
(*env)->ReleaseStringUTFChars(env, cur->jstr,
*cur->cstr);
*cur->cstr = NULL;
}
}
/*
* Given a Java array of Java strings, create a C array of C strings.
*/
char **
sarray_create(JNIEnv *env, jobjectArray array, jsize len)
{
char **result;
jobject obj;
const char *str;
jsize i;
result = calloc(len, sizeof (char *));
if (result == NULL) {
oomerror(env, "malloc failed");
return (NULL);
}
for (i = 0; i < len; i++) {
obj = (*env)->GetObjectArrayElement(env, array, i);
if ((*env)->ExceptionCheck(env) == JNI_TRUE)
goto error;
if (obj == NULL) {
npexception(env, "array element %d is null", len);
goto error;
}
str = (*env)->GetStringUTFChars(env, obj, 0);
if (str == NULL) {
(*env)->DeleteLocalRef(env, obj);
goto error;
}
result[i] = strdup(str);
(*env)->ReleaseStringUTFChars(env, obj, str);
if (result[i] == NULL) {
(*env)->DeleteLocalRef(env, obj);
oomerror(env, "strdup failed");
goto error;
}
(*env)->DeleteLocalRef(env, obj);
}
return (result);
error:
for (; i > 0; i--)
free(result[i - 1]);
free(result);
return (NULL);
}
/*
* Frees an array allocated by sarray_create.
*/
void
sarray_destroy(char **array, int len)
{
for (jsize i = 0; i < len; i++)
free(array[i]);
free(array);
}