root/usr/src/lib/libdtrace_jni/common/dtj_util.c
/*
 * 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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdlib.h>
#include <stddef.h>
#include <sys/types.h>
#include <pthread.h>
#include <string.h>
#include <dtj_util.h>

/*
 * dtj_util.c separates functionality that is generally useful from
 * that which is specific to the Java DTrace API.  If moved to a separate
 * library, this functionality could be shared by other JNI wrappers.
 */

boolean_t g_dtj_util_debug = B_FALSE;
static boolean_t g_dtj_load_common = B_FALSE;

/* NativeException */
jclass g_nx_jc = 0;
jmethodID g_nxinit_jm = 0;

/* java.io.Serializable */
jclass g_serial_jc = 0;

/* java.lang.Number */
jclass g_number_jc = 0;
jmethodID g_shortval_jm = 0;
jmethodID g_intval_jm = 0;
jmethodID g_longval_jm = 0;

/* java.lang.Byte */
jclass g_byte_jc = 0;
jmethodID g_byteinit_jm = 0;

/* java.lang.Character */
jclass g_char_jc = 0;
jmethodID g_charinit_jm = 0;
jmethodID g_charval_jm = 0;

/* java.lang.Short */
jclass g_short_jc = 0;
jmethodID g_shortinit_jm = 0;

/* java.lang.Integer */
jclass g_int_jc = 0;
jmethodID g_intinit_jm = 0;

/* java.lang.Long */
jclass g_long_jc = 0;
jmethodID g_longinit_jm = 0;

/* java.math.BigInteger */
jclass g_bigint_jc = 0;
jmethodID g_bigint_val_jsm = 0;
jmethodID g_bigint_div_jm = 0;
jmethodID g_bigint_shl_jm = 0;
jmethodID g_bigint_or_jm = 0;
jmethodID g_bigint_setbit_jm = 0;

/* java.lang.String */
jclass g_string_jc = 0;
jmethodID g_strinit_bytes_jm = 0;
jmethodID g_strbytes_jm = 0;
jmethodID g_trim_jm = 0;

/* java.lang.StringBuilder */
jclass g_buf_jc = 0;
jmethodID g_bufinit_jm = 0;
jmethodID g_buf_append_char_jm = 0;
jmethodID g_buf_append_int_jm = 0;
jmethodID g_buf_append_long_jm = 0;
jmethodID g_buf_append_str_jm = 0;
jmethodID g_buf_append_obj_jm = 0;
jmethodID g_buflen_jm = 0;
jmethodID g_bufsetlen_jm = 0;

/* java.lang.Object */
jclass g_object_jc = 0;
jmethodID g_tostring_jm = 0;
jmethodID g_equals_jm = 0;

/* java.lang.Enum */
jclass g_enum_jc = 0;
jmethodID g_enumname_jm = 0;

/* List */
jclass g_list_jc = 0;
jmethodID g_listclear_jm = 0;
jmethodID g_listadd_jm = 0;
jmethodID g_listget_jm = 0;
jmethodID g_listsize_jm = 0;

/* Global list pools */
static uu_list_pool_t *g_pointer_pool = NULL;
static uu_list_pool_t *g_string_pool = NULL;

static dtj_status_t dtj_get_jni_classes(JNIEnv *, uu_list_t *, uu_list_pool_t *,
    uu_list_pool_t *, uu_list_pool_t *, const dtj_table_entry_t *);
static dtj_status_t dtj_cache_jni_methods(JNIEnv *, dtj_java_class_t *);
static dtj_status_t dtj_cache_jni_fields(JNIEnv *, dtj_java_class_t *);

/* Constructors */
static dtj_java_class_t *dtj_java_class_create(JNIEnv *, jclass *, char *,
    uu_list_pool_t *, uu_list_pool_t *, uu_list_pool_t *);
static dtj_java_method_t *dtj_java_method_create(JNIEnv *, jmethodID *, char *,
    char *, uu_list_pool_t *);
static dtj_java_method_t *dtj_java_static_method_create(JNIEnv *, jmethodID *,
    char *, char *, uu_list_pool_t *);
static dtj_java_field_t *dtj_java_field_create(JNIEnv *, jfieldID *, char *,
    char *, uu_list_pool_t *);
static dtj_java_field_t *dtj_java_static_field_create(JNIEnv *, jfieldID *,
    char *, char *, uu_list_pool_t *);

/* Destructors */
static void dtj_java_class_destroy(void *, void *);
static void dtj_java_method_destroy(void *, void *);
static void dtj_java_field_destroy(void *, void *);

/* Comparison functions, uu_compare_fn_t signature */
static int dtj_java_class_cmp(const void *, const void *, void *);
static int dtj_java_method_cmp(const void *, const void *, void *);
static int dtj_java_field_cmp(const void *, const void *, void *);

/* Java Throwable */
static void dtj_throw(JNIEnv *, jclass, const char *, va_list *);

/* Support for uu_list_t wrappers */
static boolean_t dtj_check_pointer_pool(void);
static boolean_t dtj_check_string_pool(void);

dtj_status_t
dtj_load_common(JNIEnv *jenv)
{
        dtj_status_t status;

        static const dtj_table_entry_t table[] = {
                /* NativeException */
                { JCLASS,  &g_nx_jc,
                        "org/opensolaris/os/dtrace/NativeException" },
                { JMETHOD, &g_nxinit_jm, CONSTRUCTOR,
                        "(Ljava/lang/String;ILjava/lang/Throwable;)V" },

                /* java.io.Serializable */
                { JCLASS,  &g_serial_jc, "java/io/Serializable" },

                /* java.lang.Number */
                { JCLASS,  &g_number_jc, "java/lang/Number" },
                { JMETHOD, &g_shortval_jm, "shortValue", "()S" },
                { JMETHOD, &g_intval_jm, "intValue", "()I" },
                { JMETHOD, &g_longval_jm, "longValue", "()J" },

                /* java.lang.Byte */
                { JCLASS,  &g_byte_jc, "java/lang/Byte" },
                { JMETHOD, &g_byteinit_jm, CONSTRUCTOR, "(B)V" },

                /* java.lang.Character */
                { JCLASS,  &g_char_jc, "java/lang/Character" },
                { JMETHOD, &g_charinit_jm, CONSTRUCTOR, "(C)V" },
                { JMETHOD, &g_charval_jm, "charValue", "()C" },

                /* java.lang.Short */
                { JCLASS,  &g_short_jc, "java/lang/Short" },
                { JMETHOD, &g_shortinit_jm, CONSTRUCTOR, "(S)V" },

                /* java.lang.Integer */
                { JCLASS,  &g_int_jc, "java/lang/Integer" },
                { JMETHOD, &g_intinit_jm, CONSTRUCTOR, "(I)V" },

                /* java.lang.Long */
                { JCLASS,  &g_long_jc, "java/lang/Long" },
                { JMETHOD, &g_longinit_jm, CONSTRUCTOR, "(J)V" },

                /* java.math.BigInteger */
                { JCLASS,  &g_bigint_jc, "java/math/BigInteger" },
                { JMETHOD_STATIC, &g_bigint_val_jsm, "valueOf",
                        "(J)Ljava/math/BigInteger;" },
                { JMETHOD, &g_bigint_div_jm, "divide",
                        "(Ljava/math/BigInteger;)Ljava/math/BigInteger;" },
                { JMETHOD, &g_bigint_shl_jm, "shiftLeft",
                        "(I)Ljava/math/BigInteger;" },
                { JMETHOD, &g_bigint_or_jm, "or",
                        "(Ljava/math/BigInteger;)Ljava/math/BigInteger;" },
                { JMETHOD, &g_bigint_setbit_jm, "setBit",
                        "(I)Ljava/math/BigInteger;" },

                /* java.lang.String */
                { JCLASS,  &g_string_jc, "java/lang/String" },
                { JMETHOD, &g_strinit_bytes_jm, CONSTRUCTOR, "([B)V" },
                { JMETHOD, &g_strbytes_jm, "getBytes", "()[B" },
                { JMETHOD, &g_trim_jm, "trim", "()Ljava/lang/String;" },

                /* java.lang.StringBuilder */
                { JCLASS,  &g_buf_jc, "java/lang/StringBuilder" },
                { JMETHOD, &g_bufinit_jm, CONSTRUCTOR, "()V" },
                { JMETHOD, &g_buf_append_char_jm, "append",
                        "(C)Ljava/lang/StringBuilder;" },
                { JMETHOD, &g_buf_append_int_jm, "append",
                        "(I)Ljava/lang/StringBuilder;" },
                { JMETHOD, &g_buf_append_long_jm, "append",
                        "(J)Ljava/lang/StringBuilder;" },
                { JMETHOD, &g_buf_append_str_jm, "append",
                        "(Ljava/lang/String;)Ljava/lang/StringBuilder;" },
                { JMETHOD, &g_buf_append_obj_jm, "append",
                        "(Ljava/lang/Object;)Ljava/lang/StringBuilder;" },
                { JMETHOD, &g_buflen_jm, "length", "()I" },
                { JMETHOD, &g_bufsetlen_jm, "setLength", "(I)V" },

                /* java.lang.Object */
                { JCLASS,  &g_object_jc, "java/lang/Object" },
                { JMETHOD, &g_tostring_jm, "toString",
                        "()Ljava/lang/String;" },
                { JMETHOD, &g_equals_jm, "equals",
                        "(Ljava/lang/Object;)Z" },

                /* java.lang.Enum */
                { JCLASS,  &g_enum_jc, "java/lang/Enum" },
                { JMETHOD, &g_enumname_jm, "name",
                        "()Ljava/lang/String;" },

                /* List */
                { JCLASS, &g_list_jc, "java/util/List" },
                { JMETHOD, &g_listclear_jm, "clear", "()V" },
                { JMETHOD, &g_listadd_jm, "add", "(Ljava/lang/Object;)Z" },
                { JMETHOD, &g_listget_jm, "get", "(I)Ljava/lang/Object;" },
                { JMETHOD, &g_listsize_jm, "size", "()I" },

                { DTJ_TYPE_END }
        };

        status = dtj_cache_jni_classes(jenv, table);
        if (status == DTJ_OK) {
                g_dtj_load_common = B_TRUE;
        }
        return (status);
}

static int
/* ARGSUSED */
dtj_java_class_cmp(const void * v1, const void * v2, void *arg)
{
        const dtj_java_class_t *c1 = v1;
        const dtj_java_class_t *c2 = v2;
        return (strcmp(c1->djc_name, c2->djc_name));
}

static int
/* ARGSUSED */
dtj_java_method_cmp(const void *v1, const void *v2, void *arg)
{
        int cmp;
        const dtj_java_method_t *m1 = v1;
        const dtj_java_method_t *m2 = v2;
        cmp = strcmp(m1->djm_name, m2->djm_name);
        if (cmp == 0) {
                cmp = strcmp(m1->djm_signature, m2->djm_signature);
        }
        return (cmp);
}

static int
/* ARGSUSED */
dtj_java_field_cmp(const void *v1, const void *v2, void *arg)
{
        const dtj_java_field_t *f1 = v1;
        const dtj_java_field_t *f2 = v2;
        return (strcmp(f1->djf_name, f2->djf_name));
}

static dtj_java_class_t *
dtj_java_class_create(JNIEnv *jenv, jclass *jc, char *name,
    uu_list_pool_t *classpool, uu_list_pool_t *methodpool,
    uu_list_pool_t *fieldpool)
{
        dtj_java_class_t *c = uu_zalloc(sizeof (dtj_java_class_t));
        if (c) {
                uu_list_node_init(c, &c->djc_node, classpool);
                c->djc_ptr = jc;
                c->djc_name = name;
                c->djc_methods = uu_list_create(methodpool, NULL,
                    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
                if (!c->djc_methods) {
                        dtj_throw_out_of_memory(jenv,
                            "Failed method list creation");
                        uu_list_node_fini(c, &c->djc_node, classpool);
                        free(c);
                        c = NULL;
                }
                c->djc_fields = uu_list_create(fieldpool, NULL,
                    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
                if (!c->djc_fields) {
                        dtj_throw_out_of_memory(jenv,
                            "Failed field list creation");
                        uu_list_destroy(c->djc_methods);
                        c->djc_methods = NULL;
                        uu_list_node_fini(c, &c->djc_node, classpool);
                        free(c);
                        c = NULL;
                }
        } else {
                dtj_throw_out_of_memory(jenv,
                    "Failed to allocate class description");
        }
        return (c);
}

static dtj_java_method_t *
dtj_java_method_create(JNIEnv *jenv, jmethodID *jm, char *name, char *signature,
    uu_list_pool_t *methodpool)
{
        dtj_java_method_t *m = uu_zalloc(sizeof (dtj_java_method_t));
        if (m) {
                uu_list_node_init(m, &m->djm_node, methodpool);
                m->djm_ptr = jm;
                m->djm_name = name;
                m->djm_signature = signature;
                m->djm_static = B_FALSE;
        } else {
                dtj_throw_out_of_memory(jenv,
                    "Failed to allocate method description");
        }
        return (m);
}

static dtj_java_method_t *
dtj_java_static_method_create(JNIEnv *jenv, jmethodID *jm, char *name,
    char *signature, uu_list_pool_t *methodpool)
{
        dtj_java_method_t *m = dtj_java_method_create(jenv, jm, name, signature,
            methodpool);
        if (m) {
                m->djm_static = B_TRUE;
        }
        return (m);
}

static dtj_java_field_t *
dtj_java_field_create(JNIEnv *jenv, jfieldID *jf, char *name, char *type,
    uu_list_pool_t *fieldpool)
{
        dtj_java_field_t *f = uu_zalloc(sizeof (dtj_java_field_t));
        if (f) {
                uu_list_node_init(f, &f->djf_node, fieldpool);
                f->djf_ptr = jf;
                f->djf_name = name;
                f->djf_type = type;
                f->djf_static = B_FALSE;
        } else {
                dtj_throw_out_of_memory(jenv,
                    "Failed to allocate field description");
        }
        return (f);
}

static dtj_java_field_t *
dtj_java_static_field_create(JNIEnv *jenv, jfieldID *jf, char *name, char *type,
    uu_list_pool_t *fieldpool)
{
        dtj_java_field_t *f = dtj_java_field_create(jenv, jf, name, type,
            fieldpool);
        if (f) {
                f->djf_static = B_TRUE;
        }
        return (f);
}

static void
/* ARGSUSED */
dtj_java_class_destroy(void *v, void *arg)
{
        if (v) {
                dtj_java_class_t *c = v;
                c->djc_ptr = NULL;  /* do not free user-defined storage */
                c->djc_name = NULL; /* string literal */
                dtj_list_destroy(c->djc_methods, dtj_java_method_destroy, NULL);
                dtj_list_destroy(c->djc_fields, dtj_java_field_destroy, NULL);
                c->djc_methods = NULL;
                c->djc_fields = NULL;
                uu_free(v);
        }
}

static void
/* ARGSUSED */
dtj_java_method_destroy(void *v, void *arg)
{
        if (v) {
                dtj_java_method_t *m = v;
                m->djm_ptr = NULL;      /* do not free user-defined space */
                m->djm_name = NULL;     /* string literal */
                m->djm_signature = NULL;        /* string literal */
                uu_free(v);
        }
}

static void
/* ARGSUSED */
dtj_java_field_destroy(void *v, void *arg)
{
        if (v) {
                dtj_java_field_t *f = v;
                f->djf_ptr = NULL;  /* do not free user-defined space */
                f->djf_name = NULL; /* string literal */
                f->djf_type = NULL; /* string literal */
                uu_free(f);
        }
}

dtj_status_t
dtj_cache_jni_classes(JNIEnv *jenv, const dtj_table_entry_t *table)
{
        dtj_java_class_t *class;
        uu_list_pool_t *classpool;
        uu_list_pool_t *methodpool;
        uu_list_pool_t *fieldpool;
        uu_list_t *classes;
        uu_list_walk_t *itr;
        jclass jc;
        jclass gjc;
        dtj_status_t status;

        classpool = uu_list_pool_create("classpool",
            sizeof (dtj_java_class_t),
            offsetof(dtj_java_class_t, djc_node), dtj_java_class_cmp,
            (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
        if (!classpool) {
                dtj_throw_out_of_memory(jenv, "failed class pool creation");
                return (DTJ_ERR);
        }
        methodpool = uu_list_pool_create("methodpool",
            sizeof (dtj_java_method_t),
            offsetof(dtj_java_method_t, djm_node), dtj_java_method_cmp,
            (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
        if (!methodpool) {
                dtj_throw_out_of_memory(jenv, "failed method pool creation");
                return (DTJ_ERR);
        }
        fieldpool = uu_list_pool_create("fieldpool",
            sizeof (dtj_java_field_t),
            offsetof(dtj_java_field_t, djf_node), dtj_java_field_cmp,
            (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
        if (!fieldpool) {
                dtj_throw_out_of_memory(jenv, "failed field pool creation");
                return (DTJ_ERR);
        }

        classes = uu_list_create(classpool, NULL,
            (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
        if (!classes) {
                dtj_throw_out_of_memory(jenv, "failed class list creation");
                return (DTJ_ERR);
        }

        status = dtj_get_jni_classes(jenv, classes, classpool, methodpool,
            fieldpool, table);
        if (status != DTJ_OK) {
                /* java error pending */
                return (status);
        }

        itr = uu_list_walk_start(classes, 0);
        while ((class = uu_list_walk_next(itr)) != NULL) {
                jc = (*jenv)->FindClass(jenv, class->djc_name);
                if (!jc) {
                        /* NoClassDefFoundError pending */
                        return (DTJ_ERR);
                }
                gjc = (*jenv)->NewGlobalRef(jenv, jc);
                (*jenv)->DeleteLocalRef(jenv, jc);
                if (!gjc) {
                        dtj_throw_out_of_memory(jenv,
                            "Failed to create global class reference");
                        return (DTJ_ERR);
                }
                *(class->djc_ptr) = gjc;
                status = dtj_cache_jni_methods(jenv, class);
                if (status != DTJ_OK) {
                        /* java error pending */
                        return (status);
                }
                status = dtj_cache_jni_fields(jenv, class);
                if (status != DTJ_OK) {
                        /* java error pending */
                        return (status);
                }
        }
        uu_list_walk_end(itr);
        dtj_list_destroy(classes, dtj_java_class_destroy, NULL);
        uu_list_pool_destroy(classpool);
        uu_list_pool_destroy(methodpool);
        uu_list_pool_destroy(fieldpool);
        return (DTJ_OK);
}

/*
 * Converts JNI table entry desriptions into java_class_t descriptors.
 */
static dtj_status_t
dtj_get_jni_classes(JNIEnv *jenv, uu_list_t *classes,
    uu_list_pool_t *classpool, uu_list_pool_t *methodpool,
    uu_list_pool_t *fieldpool, const dtj_table_entry_t *table)
{
        int i;
        dtj_java_class_t *c = NULL;
        dtj_java_method_t *m;
        dtj_java_field_t *f;

        for (i = 0; table[i].djte_type != DTJ_TYPE_END; ++i) {
                /*
                 * Class not added until all of its method and field information
                 * is attached, so we defer adding a class until the next
                 * element with type JCLASS.
                 */
                switch (table[i].djte_type) {
                case JCLASS:
                        if (c) {
                                /* previous class */
                                if (!dtj_list_add(classes, c)) {
                                        dtj_throw_out_of_memory(jenv,
                                            "Failed to add class description");
                                        /*
                                         * In response to an error return value,
                                         * the caller will delete the class
                                         * descriptions list with any
                                         * descriptions created so far.
                                         */
                                        return (DTJ_ERR);
                                }
                        }
                        c = dtj_java_class_create(jenv,
                            (jclass *)table[i].djte_addr, table[i].djte_name,
                            classpool, methodpool, fieldpool);
                        if (!c) {
                                /* OutOfMemoryError pending */
                                return (DTJ_ERR);
                        }
                        break;
                case JMETHOD:
                        if (!c) {
                                dtj_throw_illegal_state(jenv,
                                    "method description not preceded "
                                    "by class description");
                                return (DTJ_ERR);
                        }
                        m = dtj_java_method_create(jenv,
                            (jmethodID *)table[i].djte_addr,
                            table[i].djte_name, table[i].djte_desc,
                            methodpool);
                        if (!m) {
                                /* OutOfMemoryError pending */
                                return (DTJ_ERR);
                        }
                        if (!dtj_list_add(c->djc_methods, m)) {
                                dtj_throw_out_of_memory(jenv,
                                    "Failed to add method description");
                                return (DTJ_ERR);
                        }
                        break;
                case JMETHOD_STATIC:
                        if (!c) {
                                dtj_throw_illegal_state(jenv,
                                    "static method description not preceded "
                                    "by class description");
                                return (DTJ_ERR);
                        }
                        m = dtj_java_static_method_create(jenv,
                            (jmethodID *)table[i].djte_addr,
                            table[i].djte_name, table[i].djte_desc,
                            methodpool);
                        if (!m) {
                                /* OutOfMemoryError pending */
                                return (DTJ_ERR);
                        }
                        if (!dtj_list_add(c->djc_methods, m)) {
                                dtj_throw_out_of_memory(jenv,
                                    "Failed to add static method description");
                                return (DTJ_ERR);
                        }
                        break;
                case JFIELD:
                        if (!c) {
                                dtj_throw_illegal_state(jenv,
                                    "field description not preceded "
                                    "by class description");
                                return (DTJ_ERR);
                        }
                        f = dtj_java_field_create(jenv,
                            (jfieldID *)table[i].djte_addr,
                            table[i].djte_name, table[i].djte_desc,
                            fieldpool);
                        if (!f) {
                                /* OutOfMemoryError pending */
                                return (DTJ_ERR);
                        }
                        if (!dtj_list_add(c->djc_fields, f)) {
                                dtj_throw_out_of_memory(jenv,
                                    "Failed to add field description");
                                return (DTJ_ERR);
                        }
                        break;
                case JFIELD_STATIC:
                        if (!c) {
                                dtj_throw_illegal_state(jenv,
                                    "static field description not preceded "
                                    "by class description");
                                return (DTJ_ERR);
                        }
                        f = dtj_java_static_field_create(jenv,
                            (jfieldID *)table[i].djte_addr,
                            table[i].djte_name, table[i].djte_desc,
                            fieldpool);
                        if (!f) {
                                /* OutOfMemoryError pending */
                                return (DTJ_ERR);
                        }
                        if (!dtj_list_add(c->djc_fields, f)) {
                                dtj_throw_out_of_memory(jenv,
                                    "Failed to add static field description");
                                return (DTJ_ERR);
                        }
                        break;
                default:
                        dtj_throw_illegal_state(jenv,
                            "Unexpected jni_type_e: %d", table[i].djte_type);
                        return (DTJ_ERR);
                }
        }
        if (c) {
                /* last class */
                if (!dtj_list_add(classes, c)) {
                        dtj_throw_out_of_memory(jenv,
                            "Failed to add class description");
                        return (DTJ_ERR);
                }
        }

        return (DTJ_OK);
}

static dtj_status_t
dtj_cache_jni_methods(JNIEnv *jenv, dtj_java_class_t *c)
{
        dtj_java_method_t *method;
        jmethodID jm;
        uu_list_walk_t *itr;
        itr = uu_list_walk_start(c->djc_methods, 0);
        while ((method = uu_list_walk_next(itr)) != NULL) {
                if (method->djm_static) {
                        jm = (*jenv)->GetStaticMethodID(jenv, *(c->djc_ptr),
                            method->djm_name, method->djm_signature);
                } else {
                        jm = (*jenv)->GetMethodID(jenv, *(c->djc_ptr),
                            method->djm_name, method->djm_signature);
                }
                if (jm == 0) {
                        /*
                         * The pending NoSuchMethodError gives only the
                         * method name, which is not so helpful for
                         * overloaded methods and methods such as <init>
                         * that have the same name in multiple classes.
                         * Clear the pending error and throw one that
                         * includes the class name and the method
                         * signature.
                         */
                        jclass jc;
                        char msg[DTJ_MSG_SIZE];
                        (*jenv)->ExceptionClear(jenv);
                        (void) snprintf(msg, sizeof (msg), "%s %s %s",
                            c->djc_name, method->djm_name,
                            method->djm_signature);

                        jc = (*jenv)->FindClass(jenv,
                            "java/lang/NoSuchMethodError");
                        (*jenv)->ThrowNew(jenv, jc, msg);
                        (*jenv)->DeleteLocalRef(jenv, jc);
                        return (DTJ_ERR);
                }
                *(method->djm_ptr) = jm;
        }
        uu_list_walk_end(itr);
        return (DTJ_OK);
}

static dtj_status_t
dtj_cache_jni_fields(JNIEnv *jenv, dtj_java_class_t *c)
{
        dtj_java_field_t *field;
        jfieldID jf;
        uu_list_walk_t *itr;
        itr = uu_list_walk_start(c->djc_fields, 0);
        while ((field = uu_list_walk_next(itr)) != NULL) {
                if (field->djf_static) {
                        jf = (*jenv)->GetStaticFieldID(jenv, *(c->djc_ptr),
                            field->djf_name, field->djf_type);
                } else {
                        jf = (*jenv)->GetFieldID(jenv, *(c->djc_ptr),
                            field->djf_name, field->djf_type);
                }
                if (jf == 0) {
                        jclass jc;
                        char msg[DTJ_MSG_SIZE];
                        (*jenv)->ExceptionClear(jenv);
                        (void) snprintf(msg, sizeof (msg),
                            "%s.%s signature: %s", c->djc_name,
                            field->djf_name, field->djf_type);

                        jc = (*jenv)->FindClass(jenv,
                            "java/lang/NoSuchFieldError");
                        (*jenv)->ThrowNew(jenv, jc, msg);
                        (*jenv)->DeleteLocalRef(jenv, jc);
                        return (DTJ_ERR);
                }
                *(field->djf_ptr) = jf;
        }
        uu_list_walk_end(itr);
        return (DTJ_OK);
}


/* Common utilities */

static void
dtj_throw(JNIEnv *jenv, jclass jc, const char *fmt, va_list *ap)
{
        char msg[DTJ_MSG_SIZE];
        (void) vsnprintf(msg, sizeof (msg), fmt, *ap);
        (*jenv)->ThrowNew(jenv, jc, msg);
}

void
dtj_throw_out_of_memory(JNIEnv *jenv, const char *fmt, ...)
{
        va_list ap;
        jclass jc;
        /*
         * JNI documentation unclear whether NewGlobalRef() can throw
         * OutOfMemoryError, so we'll make this function safe in case
         * OutOfMemoryError has already been thrown
         */
        if ((*jenv)->ExceptionCheck(jenv)) {
                return;
        }
        jc = (*jenv)->FindClass(jenv,
            "java/lang/OutOfMemoryError");
        va_start(ap, fmt);
        dtj_throw(jenv, jc, fmt, &ap);
        (*jenv)->DeleteLocalRef(jenv, jc);
        va_end(ap);
}

void
dtj_throw_null_pointer(JNIEnv *jenv, const char *fmt, ...)
{
        va_list ap;
        jclass jc = (*jenv)->FindClass(jenv,
            "java/lang/NullPointerException");
        va_start(ap, fmt);
        dtj_throw(jenv, jc, fmt, &ap);
        (*jenv)->DeleteLocalRef(jenv, jc);
        va_end(ap);
}

void
dtj_throw_illegal_state(JNIEnv *jenv, const char *fmt, ...)
{
        va_list ap;
        jclass jc = (*jenv)->FindClass(jenv,
            "java/lang/IllegalStateException");
        va_start(ap, fmt);
        dtj_throw(jenv, jc, fmt, &ap);
        (*jenv)->DeleteLocalRef(jenv, jc);
        va_end(ap);
}

void
dtj_throw_illegal_argument(JNIEnv *jenv, const char *fmt, ...)
{
        va_list ap;
        jclass jc = (*jenv)->FindClass(jenv,
            "java/lang/IllegalArgumentException");
        va_start(ap, fmt);
        dtj_throw(jenv, jc, fmt, &ap);
        (*jenv)->DeleteLocalRef(jenv, jc);
        va_end(ap);
}

void
dtj_throw_no_such_element(JNIEnv *jenv, const char *fmt, ...)
{
        va_list ap;
        jclass jc = (*jenv)->FindClass(jenv,
            "java/util/NoSuchElementException");
        va_start(ap, fmt);
        dtj_throw(jenv, jc, fmt, &ap);
        (*jenv)->DeleteLocalRef(jenv, jc);
        va_end(ap);
}

void
dtj_throw_class_cast(JNIEnv *jenv, const char *fmt, ...)
{
        va_list ap;
        jclass jc = (*jenv)->FindClass(jenv,
            "java/lang/ClassCastException");
        va_start(ap, fmt);
        dtj_throw(jenv, jc, fmt, &ap);
        (*jenv)->DeleteLocalRef(jenv, jc);
        va_end(ap);
}

void
dtj_throw_assertion(JNIEnv *jenv, const char *fmt, ...)
{
        va_list ap;
        jclass jc = (*jenv)->FindClass(jenv,
            "java/lang/AssertionError");
        va_start(ap, fmt);
        dtj_throw(jenv, jc, fmt, &ap);
        (*jenv)->DeleteLocalRef(jenv, jc);
        va_end(ap);
}

void
dtj_throw_resource_limit(JNIEnv *jenv, const char *fmt, ...)
{
        va_list ap;
        jclass jc = (*jenv)->FindClass(jenv,
            "org/opensolaris/os/dtrace/ResourceLimitException");
        va_start(ap, fmt);
        dtj_throw(jenv, jc, fmt, &ap);
        (*jenv)->DeleteLocalRef(jenv, jc);
        va_end(ap);
}

void
dtj_wrap_exception(JNIEnv *jenv, const char *file, int line)
{
        jthrowable e = NULL;
        jthrowable nx = NULL;
        jstring jfile = NULL;

        e = (*jenv)->ExceptionOccurred(jenv);
        if (!e) {
                return;
        }

        if (!g_dtj_load_common) {
                return;
        }

        (*jenv)->ExceptionClear(jenv);

        /* Unsafe to test while exception pending */
        if ((*jenv)->IsInstanceOf(jenv, e, g_nx_jc)) {
                /* Already wrapped */
                (*jenv)->Throw(jenv, e);
                (*jenv)->DeleteLocalRef(jenv, e);
                return;
        }

        jfile = dtj_NewStringNative(jenv, file);
        if ((*jenv)->ExceptionCheck(jenv)) {
                /*
                 * Only wrap the exception if possible, otherwise just throw the
                 * original exception.
                 */
                (*jenv)->ExceptionClear(jenv);
                (*jenv)->Throw(jenv, e);
                (*jenv)->DeleteLocalRef(jenv, e);
                return;
        }

        nx = (jthrowable)(*jenv)->NewObject(jenv, g_nx_jc, g_nxinit_jm,
            jfile, line, e);
        (*jenv)->DeleteLocalRef(jenv, jfile);
        if ((*jenv)->ExceptionCheck(jenv)) {
                (*jenv)->ExceptionClear(jenv);
                (*jenv)->Throw(jenv, e);
                (*jenv)->DeleteLocalRef(jenv, e);
                return;
        }

        (*jenv)->DeleteLocalRef(jenv, e);
        (*jenv)->Throw(jenv, nx);
        (*jenv)->DeleteLocalRef(jenv, nx);
}

/*
 * Calls the given java object's toString() method and prints the value to
 * stdout.  Useful for debugging.  Guaranteed that no exception is pending when
 * this function returns.
 */
void
dtj_print_object(JNIEnv *jenv, jobject jobj)
{
        jstring jstr;
        const char *cstr;

        if (!g_dtj_load_common) {
                dtj_throw_illegal_state(jenv,
                    "dtj_load_common() has not been called");
                (*jenv)->ExceptionDescribe(jenv); /* clears the exception */
                return;
        }

        if (!jobj) {
                (void) printf("null\n");
                return;
        }

        jstr = (*jenv)->CallObjectMethod(jenv, jobj, g_tostring_jm);
        if ((*jenv)->ExceptionCheck(jenv)) {
                (*jenv)->ExceptionDescribe(jenv); /* clears the exception */
                return;
        }
        cstr = (*jenv)->GetStringUTFChars(jenv, jstr, 0);
        if (cstr) {
                (void) printf("%s\n", cstr);
        } else {
                (*jenv)->ExceptionDescribe(jenv); /* clears the exception */
                (*jenv)->DeleteLocalRef(jenv, jstr);
                return;
        }
        (*jenv)->ReleaseStringUTFChars(jenv, jstr, cstr);
        (*jenv)->DeleteLocalRef(jenv, jstr);
}

jobject
dtj_uint64(JNIEnv *jenv, uint64_t u)
{
        int64_t i = (int64_t)u;
        jobject val64;

        if (i >= 0) {
                val64 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc,
                    g_bigint_val_jsm, u);
        } else {
                jobject tmp;

                u ^= ((uint64_t)0x1 << 63);
                val64 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc,
                    g_bigint_val_jsm, u);
                tmp = val64;
                val64 = (*jenv)->CallObjectMethod(jenv, tmp,
                    g_bigint_setbit_jm, 63);
                (*jenv)->DeleteLocalRef(jenv, tmp);
        }

        return (val64);
}

jobject
dtj_int128(JNIEnv *jenv, uint64_t high, uint64_t low)
{
        jobject val128;
        jobject low64;
        jobject tmp;

        val128 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc,
            g_bigint_val_jsm, high);
        tmp = val128;
        val128 = (*jenv)->CallObjectMethod(jenv, tmp, g_bigint_shl_jm, 64);
        (*jenv)->DeleteLocalRef(jenv, tmp);
        low64 = dtj_uint64(jenv, low);
        tmp = val128;
        val128 = (*jenv)->CallObjectMethod(jenv, tmp, g_bigint_or_jm, low64);
        (*jenv)->DeleteLocalRef(jenv, tmp);
        (*jenv)->DeleteLocalRef(jenv, low64);

        return (val128);
}

jstring
dtj_format_string(JNIEnv *jenv, const char *fmt, ...)
{
        va_list ap;
        char str[DTJ_MSG_SIZE];

        jstring jstr = NULL;

        va_start(ap, fmt);
        (void) vsnprintf(str, sizeof (str), fmt, ap);
        va_end(ap);

        jstr = dtj_NewStringNative(jenv, str);
        /* return NULL if OutOfMemoryError pending */
        return (jstr);
}

jstring
dtj_NewStringNative(JNIEnv *jenv, const char *str)
{
        jstring result;
        jbyteArray bytes = 0;
        int len;

        if (!g_dtj_load_common) {
                dtj_throw_illegal_state(jenv,
                    "dtj_load_common() has not been called");
                return (NULL);
        }

        len = strlen(str);

        bytes = (*jenv)->NewByteArray(jenv, len);
        if (!bytes) {
                return (NULL); /* OutOfMemoryError pending */
        }
        (*jenv)->SetByteArrayRegion(jenv, bytes, 0, len,
            (jbyte *)str);
        if ((*jenv)->ExceptionCheck(jenv)) {
                (*jenv)->DeleteLocalRef(jenv, bytes);
                return (NULL); /* ArrayIndexOutOfBoundsException pending */
        }
        result = (*jenv)->NewObject(jenv, g_string_jc, g_strinit_bytes_jm,
            bytes);
        (*jenv)->DeleteLocalRef(jenv, bytes);
        /* return NULL result if exception pending */
        return (result);
}

char *
dtj_GetStringNativeChars(JNIEnv *jenv, jstring jstr)
{
        jbyteArray bytes = NULL;

        jint len;
        char *result = NULL;

        if (!g_dtj_load_common) {
                dtj_throw_illegal_state(jenv,
                    "dtj_load_common() has not been called");
                return (NULL);
        }

        bytes = (*jenv)->CallObjectMethod(jenv, jstr, g_strbytes_jm);
        if ((*jenv)->ExceptionCheck(jenv)) {
                return (NULL); /* OutOfMemoryError pending */
        }
        /* Does not throw exceptions */
        len = (*jenv)->GetArrayLength(jenv, bytes);
        result = malloc(len + 1);
        if (!result) {
                (*jenv)->DeleteLocalRef(jenv, bytes);
                dtj_throw_out_of_memory(jenv,
                    "could not allocate native chars");
                return (NULL);
        }

        /* Skip check for ArrayIndexOutOfBoundsException */
        (*jenv)->GetByteArrayRegion(jenv, bytes, 0, len,
            (jbyte *)result);
        (*jenv)->DeleteLocalRef(jenv, bytes);
        result[len] = '\0'; /* NUL-terminate */

        return (result);
}

void
/* ARGSUSED */
dtj_ReleaseStringNativeChars(JNIEnv *jenv, jstring jstr, const char *str)
{
        free((void *)str);
}

char **
dtj_get_argv(JNIEnv *jenv, jobjectArray args, int *argc)
{
        char **argv = NULL; /* return value */
        const char *str;
        int i;

        jstring jstr = NULL;

        if (!g_dtj_load_common) {
                dtj_throw_illegal_state(jenv,
                    "dtj_load_common() has not been called");
                return (NULL);
        }

        *argc = (*jenv)->GetArrayLength(jenv, args);
        /*
         * Initialize all string pointers to NULL so that in case of an error
         * filling in the array, free_argv() will not attempt to free the
         * unallocated elements.  Also NULL-terminate the string array for
         * functions that expect terminating NULL rather than rely on argc.
         */
        argv = uu_zalloc((sizeof (char *)) * (*argc + 1));
        if (!argv) {
                dtj_throw_out_of_memory(jenv, "Failed to allocate args array");
                return (NULL);
        }

        for (i = 0; i < *argc; ++i) {
                jstr = (*jenv)->GetObjectArrayElement(jenv, args, i);
                if ((*jenv)->ExceptionCheck(jenv)) {
                        dtj_free_argv(argv);
                        return (NULL);
                }
                str = dtj_GetStringNativeChars(jenv, jstr);
                if ((*jenv)->ExceptionCheck(jenv)) {
                        dtj_free_argv(argv);
                        (*jenv)->DeleteLocalRef(jenv, jstr);
                        return (NULL);
                }
                argv[i] = malloc(strlen(str) + 1);
                if (!argv[i]) {
                        dtj_throw_out_of_memory(jenv, "Failed to allocate arg");
                        dtj_free_argv(argv);
                        dtj_ReleaseStringNativeChars(jenv, jstr, str);
                        (*jenv)->DeleteLocalRef(jenv, jstr);
                        return (NULL);
                }
                (void) strcpy(argv[i], str);
                dtj_ReleaseStringNativeChars(jenv, jstr, str);
                (*jenv)->DeleteLocalRef(jenv, jstr);
                jstr = NULL;
        }

        return (argv);
}

char **
dtj_make_argv(JNIEnv *jenv, jstring command, int *argc)
{
        const char *ws = "\f\n\r\t\v ";
        char **argv = NULL; /* return value */
        const char *cmd; /* native command string */
        char *s; /* writable command */
        char *tok; /* token */
        int len;

        if (!g_dtj_load_common) {
                dtj_throw_illegal_state(jenv,
                    "dtj_load_common() has not been called");
                return (NULL);
        }

        if (!command) {
                dtj_throw_null_pointer(jenv, "command is null");
                return (NULL);
        } else if ((*jenv)->GetStringLength(jenv, command) == 0) {
                dtj_throw_illegal_argument(jenv, "command is empty");
                return (NULL);
        }

        cmd = dtj_GetStringNativeChars(jenv, command);
        if ((*jenv)->ExceptionCheck(jenv)) {
                return (NULL);
        }
        len = strlen(cmd);
        s = malloc(len + 1);
        if (!s) {
                dtj_throw_out_of_memory(jenv,
                    "failed to allocate command string");
                dtj_ReleaseStringNativeChars(jenv, command, cmd);
                return (NULL);
        }
        (void) strcpy(s, cmd);
        /*
         * Initialize all string pointers to NULL so that in case of an error
         * filling in the array, free_argv() will not attempt to free the
         * unallocated elements.  Also NULL-terminate the string array for
         * functions that expect terminating NULL rather than rely on argc.
         * Allow for maximum length resulting from single-character tokens
         * separated by single spaces.
         */
        argv = uu_zalloc(sizeof (char *) * (len / 2 + 1));
        if (!argv) {
                dtj_throw_out_of_memory(jenv, "failed to allocate args array");
                free(s);
                dtj_ReleaseStringNativeChars(jenv, command, cmd);
                return (NULL);
        }

        *argc = 0;
        for (tok = strtok(s, ws); tok != NULL; tok = strtok(NULL, ws)) {
                argv[*argc] = malloc(strlen(tok) + 1);
                if (!argv[*argc]) {
                        dtj_throw_out_of_memory(jenv, "Failed to allocate arg");
                        dtj_free_argv(argv);
                        free(s);
                        dtj_ReleaseStringNativeChars(jenv, command, cmd);
                        return (NULL);
                }
                (void) strcpy(argv[(*argc)++], tok);
        }

        if (*argc == 0) {
                dtj_throw_illegal_argument(jenv, "command is blank");
                dtj_free_argv(argv);
                free(s);
                dtj_ReleaseStringNativeChars(jenv, command, cmd);
                return (NULL);
        }

        free(s);
        dtj_ReleaseStringNativeChars(jenv, command, cmd);
        return (argv);
}

void
dtj_free_argv(char **argv)
{
        if (argv) {
                char **s = argv;
                while (*s) {
                        free((void *)*s);
                        *s++ = NULL;
                }
                free((void *)argv);
        }
}


/* Wrappers for uu_list_t */

int
/* ARGSUSED */
dtj_pointer_list_entry_cmp(const void *v1, const void *v2, void *arg)
{
        const dtj_pointer_list_entry_t *p1 = v1;
        const dtj_pointer_list_entry_t *p2 = v2;

        /*
         * It is not valid to compare pointers using the relational operators
         * unless they point to elements in the same array.
         */
        uint64_t x = (uintptr_t)p1->dple_ptr;
        uint64_t y = (uintptr_t)p2->dple_ptr;
        int rc;
        rc = ((x > y) ? 1 : ((x < y) ? -1 : 0));
        return (rc);
}

int
/* ARGSUSED */
dtj_string_list_entry_cmp(const void *v1, const void *v2, void *arg)
{
        const dtj_string_list_entry_t *p1 = v1;
        const dtj_string_list_entry_t *p2 = v2;
        const char *s1 = p1->dsle_value;
        const char *s2 = p2->dsle_value;
        if (s1 == NULL) {
                return (s2 == NULL ? 0 : -1);
        }
        if (s2 == NULL) {
                return (1);
        }
        return (strcmp(s1, s2));
}

static boolean_t
dtj_check_pointer_pool(void)
{
        if (g_pointer_pool == NULL) {
                g_pointer_pool = uu_list_pool_create("g_pointer_pool",
                    sizeof (dtj_pointer_list_entry_t),
                    offsetof(dtj_pointer_list_entry_t, dple_node),
                    dtj_pointer_list_entry_cmp,
                    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
                if (g_pointer_pool == NULL) {
                        return (B_FALSE);
                }
        }
        return (B_TRUE);
}

uu_list_t *
dtj_pointer_list_create(void)
{
        uu_list_t *list;

        if (!dtj_check_pointer_pool()) {
                return (NULL);
        }

        list = uu_list_create(g_pointer_pool, NULL,
            (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
        return (list);
}

dtj_pointer_list_entry_t *
dtj_pointer_list_entry_create(void *p)
{
        dtj_pointer_list_entry_t *e;

        if (!dtj_check_pointer_pool()) {
                return (NULL);
        }

        e = uu_zalloc(sizeof (dtj_pointer_list_entry_t));
        if (e) {
                uu_list_node_init(e, &e->dple_node, g_pointer_pool);
                e->dple_ptr = p;
        }
        return (e);
}

static boolean_t
dtj_check_string_pool(void)
{
        if (g_string_pool == NULL) {
                g_string_pool = uu_list_pool_create("g_string_pool",
                    sizeof (dtj_string_list_entry_t),
                    offsetof(dtj_string_list_entry_t, dsle_node),
                    dtj_string_list_entry_cmp,
                    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
                if (g_string_pool == NULL) {
                        return (B_FALSE);
                }
        }
        return (B_TRUE);
}

uu_list_t *
dtj_string_list_create(void)
{
        uu_list_t *list;

        if (!dtj_check_string_pool()) {
                return (NULL);
        }

        list = uu_list_create(g_string_pool, NULL,
            (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
        return (list);
}

dtj_string_list_entry_t *
dtj_string_list_entry_create(const char *s)
{
        dtj_string_list_entry_t *e;

        if (!dtj_check_string_pool()) {
                return (NULL);
        }

        e = uu_zalloc(sizeof (dtj_string_list_entry_t));
        if (e) {
                uu_list_node_init(e, &e->dsle_node, g_string_pool);
                if (s) {
                        e->dsle_value = malloc(strlen(s) + 1);
                        if (e->dsle_value) {
                                (void) strcpy(e->dsle_value, s);
                        } else {
                                uu_list_node_fini(e, &e->dsle_node,
                                    g_string_pool);
                                uu_free(e);
                                e = NULL;
                        }
                }
        }
        return (e);
}

void
dtj_pointer_list_entry_destroy(void *v,
    dtj_value_destroy_f *value_destroy, void *arg)
{
        if (v) {
                dtj_pointer_list_entry_t *e = v;
                if (value_destroy) {
                        value_destroy(e->dple_ptr, arg);
                }
                uu_list_node_fini(e, &e->dple_node, g_pointer_pool);
                e->dple_ptr = NULL;
                uu_free(v);
        }
}

void
/* ARGSUSED */
dtj_string_list_entry_destroy(void *v, void *arg)
{
        if (v) {
                dtj_string_list_entry_t *e = v;
                free(e->dsle_value);
                uu_list_node_fini(e, &e->dsle_node, g_string_pool);
                e->dsle_value = NULL;
                uu_free(v);
        }
}

void
dtj_list_clear(uu_list_t *list, dtj_value_destroy_f *value_destroy,
    void *arg)
{
        void *cookie; /* needed for uu_list_teardown */
        void *value;

        if (!list) {
                return;
        }

        cookie = NULL;
        if (value_destroy) {
                while ((value = uu_list_teardown(list, &cookie)) != NULL) {
                        value_destroy(value, arg);
                }
        } else {
                while ((value = uu_list_teardown(list, &cookie)) != NULL) {
                }
        }
}

void
dtj_list_destroy(uu_list_t *list,
    dtj_value_destroy_f *value_destroy, void *arg)
{
        dtj_list_clear(list, value_destroy, arg);
        uu_list_destroy(list);
}

void
dtj_pointer_list_clear(uu_list_t *list,
    dtj_value_destroy_f *value_destroy, void *arg)
{
        void *cookie; /* needed for uu_list_teardown */
        dtj_pointer_list_entry_t *e;

        if (!list) {
                return;
        }

        cookie = NULL;
        while ((e = uu_list_teardown(list, &cookie)) != NULL) {
                dtj_pointer_list_entry_destroy(e, value_destroy, arg);
        }
}

void
dtj_pointer_list_destroy(uu_list_t *list,
    dtj_value_destroy_f *value_destroy, void *arg)
{
        dtj_pointer_list_clear(list, value_destroy, arg);
        uu_list_destroy(list);
}

void
dtj_string_list_clear(uu_list_t *list)
{
        dtj_list_clear(list, dtj_string_list_entry_destroy, NULL);
}

void
dtj_string_list_destroy(uu_list_t *list)
{
        dtj_list_destroy(list, dtj_string_list_entry_destroy, NULL);
}

boolean_t
dtj_list_empty(uu_list_t *list)
{
        return (uu_list_numnodes(list) == 0);
}

boolean_t
dtj_list_add(uu_list_t *list, void *value)
{
        return (uu_list_insert_before(list, NULL, value) == 0);
}

boolean_t
dtj_pointer_list_add(uu_list_t *list, void *p)
{
        dtj_pointer_list_entry_t *e = dtj_pointer_list_entry_create(p);
        if (!e) {
                return (B_FALSE);
        }
        return (dtj_list_add(list, e));
}

void *
dtj_pointer_list_walk_next(uu_list_walk_t *itr)
{
        dtj_pointer_list_entry_t *e = uu_list_walk_next(itr);
        if (!e) {
                return (DTJ_INVALID_PTR);
        }
        return (e->dple_ptr);
}

void *
dtj_pointer_list_first(uu_list_t *list)
{
        dtj_pointer_list_entry_t *e = uu_list_first(list);
        if (!e) {
                /* NULL is a valid value; use -1 for invalid */
                return (DTJ_INVALID_PTR);
        }
        return (e->dple_ptr);
}

void *
dtj_pointer_list_last(uu_list_t *list)
{
        dtj_pointer_list_entry_t *e = uu_list_last(list);
        if (!e) {
                /* NULL is a valid value; use -1 for invalid */
                return (DTJ_INVALID_PTR);
        }
        return (e->dple_ptr);
}

boolean_t
dtj_string_list_add(uu_list_t *list, const char *s)
{
        dtj_string_list_entry_t *e = dtj_string_list_entry_create(s);
        if (!e) {
                return (B_FALSE);
        }
        return (dtj_list_add(list, e));
}

const char *
dtj_string_list_walk_next(uu_list_walk_t *itr)
{
        dtj_string_list_entry_t *e = uu_list_walk_next(itr);
        if (!e) {
                return (DTJ_INVALID_STR);
        }
        return (e->dsle_value);
}

const char *
dtj_string_list_first(uu_list_t *list)
{
        dtj_string_list_entry_t *e = uu_list_first(list);
        if (!e) {
                /* NULL is a valid string value; use -1 for invalid */
                return (DTJ_INVALID_STR);
        }
        return (e->dsle_value);
}

const char *
dtj_string_list_last(uu_list_t *list)
{
        dtj_string_list_entry_t *e = uu_list_last(list);
        if (!e) {
                /* NULL is a valid string value; use -1 for invalid */
                return (DTJ_INVALID_STR);
        }
        return (e->dsle_value);
}